raygui-widget
miniaudio.h
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);
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
5125 
5126 MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut);
5127 
5128 
5129 typedef struct
5130 {
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;
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. */
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. */
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 {
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;
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);
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. */
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;
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;
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;
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;
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;
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;
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 {
5842  ma_format format;
5843  ma_uint32 channels;
5844  ma_uint32 sampleRate;
5845  ma_uint64 cursor;
5846  ma_uint64 sizeInFrames;
5847  const void* pData;
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;
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. */
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. */
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 {
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;
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 {
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);
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 {
6239  ma_bool32 signalled;
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 {
6254 #ifndef MA_NO_THREADING
6255  ma_event e;
6256 #endif
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. */
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. */
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;
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. */
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;
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;
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;
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;
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;
7300 
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 
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);
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. */
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);
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;
9915 
9916 struct ma_decoder
9917 {
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;
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;
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 {
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;
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 {
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;
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. */
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;
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  {
10343  ma_uint64 decodedFrameCount;
10344  ma_uint32 sampleRate;
10345  } decodedPaged;
10346  } backend;
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_*()). */
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;
10441 
10442 MA_API ma_resource_manager_config ma_resource_manager_config_init(void);
10443 
10444 struct ma_resource_manager
10445 {
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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. */
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
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. */
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. */
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 {
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 (;;) {
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  hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
21477  if (FAILED(hr)) {
21478  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n");
21479  return ma_result_from_HRESULT(hr);
21480  }
21481 
21482  if (pDeviceID == NULL) {
21483  hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
21484  } else {
21485  hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
21486  }
21487 
21488  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
21489  if (FAILED(hr)) {
21490  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n");
21491  return ma_result_from_HRESULT(hr);
21492  }
21493 
21494  return MA_SUCCESS;
21495 }
21496 
21497 static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID)
21498 {
21499  WCHAR* pDeviceIDString;
21500  HRESULT hr;
21501 
21502  MA_ASSERT(pDeviceID != NULL);
21503 
21504  hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString);
21505  if (SUCCEEDED(hr)) {
21506  size_t idlen = ma_strlen_WCHAR(pDeviceIDString);
21507  if (idlen+1 > ma_countof(pDeviceID->wasapi)) {
21508  ma_CoTaskMemFree(pContext, pDeviceIDString);
21509  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. */
21510  return MA_ERROR;
21511  }
21512 
21513  MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t));
21514  pDeviceID->wasapi[idlen] = '\0';
21515 
21516  ma_CoTaskMemFree(pContext, pDeviceIDString);
21517 
21518  return MA_SUCCESS;
21519  }
21520 
21521  return MA_ERROR;
21522 }
21523 
21524 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)
21525 {
21526  ma_result result;
21527  HRESULT hr;
21528 
21529  MA_ASSERT(pContext != NULL);
21530  MA_ASSERT(pMMDevice != NULL);
21531  MA_ASSERT(pInfo != NULL);
21532 
21533  /* ID. */
21534  result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);
21535  if (result == MA_SUCCESS) {
21536  if (pDefaultDeviceID != NULL) {
21537  if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) {
21538  pInfo->isDefault = MA_TRUE;
21539  }
21540  }
21541  }
21542 
21543  /* Description / Friendly Name */
21544  {
21545  ma_IPropertyStore *pProperties;
21546  hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
21547  if (SUCCEEDED(hr)) {
21548  MA_PROPVARIANT var;
21549 
21550  ma_PropVariantInit(&var);
21551  hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
21552  if (SUCCEEDED(hr)) {
21553  WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
21554  ma_PropVariantClear(pContext, &var);
21555  }
21556 
21557  ma_IPropertyStore_Release(pProperties);
21558  }
21559  }
21560 
21561  /* Format */
21562  if (!onlySimpleInfo) {
21563  ma_IAudioClient* pAudioClient;
21564  hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
21565  if (SUCCEEDED(hr)) {
21566  result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo);
21567 
21568  ma_IAudioClient_Release(pAudioClient);
21569  return result;
21570  } else {
21571  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.");
21572  return ma_result_from_HRESULT(hr);
21573  }
21574  }
21575 
21576  return MA_SUCCESS;
21577 }
21578 
21579 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)
21580 {
21581  ma_result result = MA_SUCCESS;
21582  UINT deviceCount;
21583  HRESULT hr;
21584  ma_uint32 iDevice;
21585  WCHAR* pDefaultDeviceID = NULL;
21586  ma_IMMDeviceCollection* pDeviceCollection = NULL;
21587 
21588  MA_ASSERT(pContext != NULL);
21589  MA_ASSERT(callback != NULL);
21590 
21591  /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
21592  pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
21593 
21594  /* We need to enumerate the devices which returns a device collection. */
21595  hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
21596  if (SUCCEEDED(hr)) {
21597  hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
21598  if (FAILED(hr)) {
21599  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n");
21600  result = ma_result_from_HRESULT(hr);
21601  goto done;
21602  }
21603 
21604  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
21605  ma_device_info deviceInfo;
21606  ma_IMMDevice* pMMDevice;
21607 
21608  MA_ZERO_OBJECT(&deviceInfo);
21609 
21610  hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
21611  if (SUCCEEDED(hr)) {
21612  result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
21613 
21614  ma_IMMDevice_Release(pMMDevice);
21615  if (result == MA_SUCCESS) {
21616  ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
21617  if (cbResult == MA_FALSE) {
21618  break;
21619  }
21620  }
21621  }
21622  }
21623  }
21624 
21625 done:
21626  if (pDefaultDeviceID != NULL) {
21627  ma_CoTaskMemFree(pContext, pDefaultDeviceID);
21628  pDefaultDeviceID = NULL;
21629  }
21630 
21631  if (pDeviceCollection != NULL) {
21632  ma_IMMDeviceCollection_Release(pDeviceCollection);
21633  pDeviceCollection = NULL;
21634  }
21635 
21636  return result;
21637 }
21638 
21639 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)
21640 {
21641  ma_result result;
21642  HRESULT hr;
21643 
21644  MA_ASSERT(pContext != NULL);
21645  MA_ASSERT(ppAudioClient != NULL);
21646  MA_ASSERT(ppMMDevice != NULL);
21647 
21648  result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
21649  if (result != MA_SUCCESS) {
21650  return result;
21651  }
21652 
21653  hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient);
21654  if (FAILED(hr)) {
21655  return ma_result_from_HRESULT(hr);
21656  }
21657 
21658  return MA_SUCCESS;
21659 }
21660 #else
21661 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)
21662 {
21663  ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
21664  ma_completion_handler_uwp completionHandler;
21665  IID iid;
21666  WCHAR* iidStr;
21667  HRESULT hr;
21668  ma_result result;
21669  HRESULT activateResult;
21670  ma_IUnknown* pActivatedInterface;
21671 
21672  MA_ASSERT(pContext != NULL);
21673  MA_ASSERT(ppAudioClient != NULL);
21674 
21675  if (pDeviceID != NULL) {
21676  iidStr = (WCHAR*)pDeviceID->wasapi;
21677  } else {
21678  if (deviceType == ma_device_type_capture) {
21679  iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
21680  } else {
21681  iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
21682  }
21683 
21684  #if defined(__cplusplus)
21685  hr = StringFromIID(iid, &iidStr);
21686  #else
21687  hr = StringFromIID(&iid, &iidStr);
21688  #endif
21689  if (FAILED(hr)) {
21690  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");
21691  return ma_result_from_HRESULT(hr);
21692  }
21693  }
21694 
21695  result = ma_completion_handler_uwp_init(&completionHandler);
21696  if (result != MA_SUCCESS) {
21697  ma_CoTaskMemFree(pContext, iidStr);
21698  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n");
21699  return result;
21700  }
21701 
21702  hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
21703  if (FAILED(hr)) {
21704  ma_completion_handler_uwp_uninit(&completionHandler);
21705  ma_CoTaskMemFree(pContext, iidStr);
21706  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n");
21707  return ma_result_from_HRESULT(hr);
21708  }
21709 
21710  if (pDeviceID == NULL) {
21711  ma_CoTaskMemFree(pContext, iidStr);
21712  }
21713 
21714  /* Wait for the async operation for finish. */
21715  ma_completion_handler_uwp_wait(&completionHandler);
21716  ma_completion_handler_uwp_uninit(&completionHandler);
21717 
21718  hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
21719  ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
21720 
21721  if (FAILED(hr) || FAILED(activateResult)) {
21722  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n");
21723  return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult);
21724  }
21725 
21726  /* Here is where we grab the IAudioClient interface. */
21727  hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
21728  if (FAILED(hr)) {
21729  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n");
21730  return ma_result_from_HRESULT(hr);
21731  }
21732 
21733  if (ppActivatedInterface) {
21734  *ppActivatedInterface = pActivatedInterface;
21735  } else {
21736  ma_IUnknown_Release(pActivatedInterface);
21737  }
21738 
21739  return MA_SUCCESS;
21740 }
21741 #endif
21742 
21743 
21744 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */
21745 typedef enum
21746 {
21747  MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT,
21748  MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK
21749 } MA_AUDIOCLIENT_ACTIVATION_TYPE;
21750 
21751 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */
21752 typedef enum
21753 {
21754  MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE,
21755  MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE
21756 } MA_PROCESS_LOOPBACK_MODE;
21757 
21758 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */
21759 typedef struct
21760 {
21761  DWORD TargetProcessId;
21762  MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode;
21763 } MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS;
21764 
21765 #if defined(_MSC_VER) && !defined(__clang__)
21766  #pragma warning(push)
21767  #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
21768 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
21769  #pragma GCC diagnostic push
21770  #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
21771  #if defined(__clang__)
21772  #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
21773  #endif
21774 #endif
21775 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */
21776 typedef struct
21777 {
21778  MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType;
21779  union
21780  {
21781  MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams;
21782  };
21783 } MA_AUDIOCLIENT_ACTIVATION_PARAMS;
21784 #if defined(_MSC_VER) && !defined(__clang__)
21785  #pragma warning(pop)
21786 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
21787  #pragma GCC diagnostic pop
21788 #endif
21789 
21790 #define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback"
21791 
21792 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)
21793 {
21794  ma_result result;
21795  ma_bool32 usingProcessLoopback = MA_FALSE;
21796  MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams;
21797  MA_PROPVARIANT activationParams;
21798  MA_PROPVARIANT* pActivationParams = NULL;
21799  ma_device_id virtualDeviceID;
21800 
21801  /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */
21802  if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) {
21803  usingProcessLoopback = MA_TRUE;
21804  }
21805 
21806  if (usingProcessLoopback) {
21807  MA_ZERO_OBJECT(&audioclientActivationParams);
21808  audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK;
21809  audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE;
21810  audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID;
21811 
21812  ma_PropVariantInit(&activationParams);
21813  activationParams.vt = MA_VT_BLOB;
21814  activationParams.blob.cbSize = sizeof(audioclientActivationParams);
21815  activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams;
21816  pActivationParams = &activationParams;
21817 
21818  /* When requesting a specific device ID we need to use a special device ID. */
21819  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. */
21820  pDeviceID = &virtualDeviceID;
21821  } else {
21822  pActivationParams = NULL; /* No activation parameters required. */
21823  }
21824 
21825 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21826  result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
21827 #else
21828  result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
21829 #endif
21830 
21831  /*
21832  If loopback mode was requested with a process ID and initialization failed, it could be because it's
21833  trying to run on an older version of Windows where it's not supported. We need to let the caller
21834  know about this with a log message.
21835  */
21836  if (result != MA_SUCCESS) {
21837  if (usingProcessLoopback) {
21838  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);
21839  }
21840  }
21841 
21842  return result;
21843 }
21844 
21845 
21846 static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
21847 {
21848  /* Different enumeration for desktop and UWP. */
21849 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21850  /* Desktop */
21851  HRESULT hr;
21852  ma_IMMDeviceEnumerator* pDeviceEnumerator;
21853 
21854  hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
21855  if (FAILED(hr)) {
21856  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
21857  return ma_result_from_HRESULT(hr);
21858  }
21859 
21860  ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
21861  ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
21862 
21863  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
21864 #else
21865  /*
21866  UWP
21867 
21868  The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
21869  over devices without using MMDevice, I'm restricting devices to defaults.
21870 
21871  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/
21872  */
21873  if (callback) {
21874  ma_bool32 cbResult = MA_TRUE;
21875 
21876  /* Playback. */
21877  if (cbResult) {
21878  ma_device_info deviceInfo;
21879  MA_ZERO_OBJECT(&deviceInfo);
21880  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
21881  deviceInfo.isDefault = MA_TRUE;
21882  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
21883  }
21884 
21885  /* Capture. */
21886  if (cbResult) {
21887  ma_device_info deviceInfo;
21888  MA_ZERO_OBJECT(&deviceInfo);
21889  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
21890  deviceInfo.isDefault = MA_TRUE;
21891  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
21892  }
21893  }
21894 #endif
21895 
21896  return MA_SUCCESS;
21897 }
21898 
21899 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)
21900 {
21901 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21902  ma_result result;
21903  ma_IMMDevice* pMMDevice = NULL;
21904  WCHAR* pDefaultDeviceID = NULL;
21905 
21906  result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
21907  if (result != MA_SUCCESS) {
21908  return result;
21909  }
21910 
21911  /* We need the default device ID so we can set the isDefault flag in the device info. */
21912  pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
21913 
21914  result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
21915 
21916  if (pDefaultDeviceID != NULL) {
21917  ma_CoTaskMemFree(pContext, pDefaultDeviceID);
21918  pDefaultDeviceID = NULL;
21919  }
21920 
21921  ma_IMMDevice_Release(pMMDevice);
21922 
21923  return result;
21924 #else
21925  ma_IAudioClient* pAudioClient;
21926  ma_result result;
21927 
21928  /* UWP currently only uses default devices. */
21929  if (deviceType == ma_device_type_playback) {
21930  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
21931  } else {
21932  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
21933  }
21934 
21935  result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL);
21936  if (result != MA_SUCCESS) {
21937  return result;
21938  }
21939 
21940  result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);
21941 
21942  pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */
21943 
21944  ma_IAudioClient_Release(pAudioClient);
21945  return result;
21946 #endif
21947 }
21948 
21949 static ma_result ma_device_uninit__wasapi(ma_device* pDevice)
21950 {
21951  MA_ASSERT(pDevice != NULL);
21952 
21953 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21954  if (pDevice->wasapi.pDeviceEnumerator) {
21955  ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
21956  ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
21957  }
21958 #endif
21959 
21960  if (pDevice->wasapi.pRenderClient) {
21961  if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
21962  ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
21963  pDevice->wasapi.pMappedBufferPlayback = NULL;
21964  pDevice->wasapi.mappedBufferPlaybackCap = 0;
21965  pDevice->wasapi.mappedBufferPlaybackLen = 0;
21966  }
21967 
21968  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
21969  }
21970  if (pDevice->wasapi.pCaptureClient) {
21971  if (pDevice->wasapi.pMappedBufferCapture != NULL) {
21972  ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
21973  pDevice->wasapi.pMappedBufferCapture = NULL;
21974  pDevice->wasapi.mappedBufferCaptureCap = 0;
21975  pDevice->wasapi.mappedBufferCaptureLen = 0;
21976  }
21977 
21978  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
21979  }
21980 
21981  if (pDevice->wasapi.pAudioClientPlayback) {
21982  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
21983  }
21984  if (pDevice->wasapi.pAudioClientCapture) {
21985  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
21986  }
21987 
21988  if (pDevice->wasapi.hEventPlayback) {
21989  CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback);
21990  }
21991  if (pDevice->wasapi.hEventCapture) {
21992  CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
21993  }
21994 
21995  return MA_SUCCESS;
21996 }
21997 
21998 
21999 typedef struct
22000 {
22001  /* Input. */
22002  ma_format formatIn;
22003  ma_uint32 channelsIn;
22004  ma_uint32 sampleRateIn;
22005  ma_channel channelMapIn[MA_MAX_CHANNELS];
22006  ma_uint32 periodSizeInFramesIn;
22007  ma_uint32 periodSizeInMillisecondsIn;
22008  ma_uint32 periodsIn;
22009  ma_share_mode shareMode;
22010  ma_performance_profile performanceProfile;
22011  ma_bool32 noAutoConvertSRC;
22012  ma_bool32 noDefaultQualitySRC;
22013  ma_bool32 noHardwareOffloading;
22014  ma_uint32 loopbackProcessID;
22015  ma_bool32 loopbackProcessExclude;
22016 
22017  /* Output. */
22018  ma_IAudioClient* pAudioClient;
22019  ma_IAudioRenderClient* pRenderClient;
22020  ma_IAudioCaptureClient* pCaptureClient;
22021  ma_format formatOut;
22022  ma_uint32 channelsOut;
22023  ma_uint32 sampleRateOut;
22024  ma_channel channelMapOut[MA_MAX_CHANNELS];
22025  ma_uint32 periodSizeInFramesOut;
22026  ma_uint32 periodsOut;
22027  ma_bool32 usingAudioClient3;
22028  char deviceName[256];
22029  ma_device_id id;
22030 } ma_device_init_internal_data__wasapi;
22031 
22032 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)
22033 {
22034  HRESULT hr;
22035  ma_result result = MA_SUCCESS;
22036  const char* errorMsg = "";
22037  MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
22038  DWORD streamFlags = 0;
22039  MA_REFERENCE_TIME periodDurationInMicroseconds;
22040  ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
22041  MA_WAVEFORMATEXTENSIBLE wf;
22042  ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
22043  ma_IAudioClient2* pAudioClient2;
22044  ma_uint32 nativeSampleRate;
22045  ma_bool32 usingProcessLoopback = MA_FALSE;
22046 
22047  MA_ASSERT(pContext != NULL);
22048  MA_ASSERT(pData != NULL);
22049 
22050  /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
22051  if (deviceType == ma_device_type_duplex) {
22052  return MA_INVALID_ARGS;
22053  }
22054 
22055  usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL;
22056 
22057  pData->pAudioClient = NULL;
22058  pData->pRenderClient = NULL;
22059  pData->pCaptureClient = NULL;
22060 
22061  streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
22062  if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
22063  streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
22064  }
22065  if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
22066  streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
22067  }
22068  if (deviceType == ma_device_type_loopback) {
22069  streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
22070  }
22071 
22072  result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface);
22073  if (result != MA_SUCCESS) {
22074  goto done;
22075  }
22076 
22077  MA_ZERO_OBJECT(&wf);
22078 
22079  /* Try enabling hardware offloading. */
22080  if (!pData->noHardwareOffloading) {
22081  hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
22082  if (SUCCEEDED(hr)) {
22083  BOOL isHardwareOffloadingSupported = 0;
22084  hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
22085  if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
22086  ma_AudioClientProperties clientProperties;
22087  MA_ZERO_OBJECT(&clientProperties);
22088  clientProperties.cbSize = sizeof(clientProperties);
22089  clientProperties.bIsOffload = 1;
22090  clientProperties.eCategory = MA_AudioCategory_Other;
22091  ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
22092  }
22093 
22094  pAudioClient2->lpVtbl->Release(pAudioClient2);
22095  }
22096  }
22097 
22098  /* 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. */
22099  result = MA_FORMAT_NOT_SUPPORTED;
22100  if (pData->shareMode == ma_share_mode_exclusive) {
22101  #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22102  /* In exclusive mode on desktop we always use the backend's native format. */
22103  ma_IPropertyStore* pStore = NULL;
22104  hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
22105  if (SUCCEEDED(hr)) {
22106  MA_PROPVARIANT prop;
22107  ma_PropVariantInit(&prop);
22108  hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
22109  if (SUCCEEDED(hr)) {
22110  MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData;
22111  hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
22112  if (SUCCEEDED(hr)) {
22113  MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));
22114  }
22115 
22116  ma_PropVariantClear(pContext, &prop);
22117  }
22118 
22119  ma_IPropertyStore_Release(pStore);
22120  }
22121  #else
22122  /*
22123  I do not know how to query the device's native format on UWP so for now I'm just disabling support for
22124  exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
22125  until you find one that works.
22126 
22127  TODO: Add support for exclusive mode to UWP.
22128  */
22129  hr = S_FALSE;
22130  #endif
22131 
22132  if (hr == S_OK) {
22133  shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
22134  result = MA_SUCCESS;
22135  } else {
22136  result = MA_SHARE_MODE_NOT_SUPPORTED;
22137  }
22138  } else {
22139  /* In shared mode we are always using the format reported by the operating system. */
22140  MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
22141  hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat);
22142  if (hr != S_OK) {
22143  /* When using process-specific loopback, GetMixFormat() seems to always fail. */
22144  if (usingProcessLoopback) {
22145  wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
22146  wf.nChannels = 2;
22147  wf.nSamplesPerSec = 44100;
22148  wf.wBitsPerSample = 32;
22149  wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
22150  wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
22151  wf.cbSize = sizeof(MA_WAVEFORMATEX);
22152 
22153  result = MA_SUCCESS;
22154  } else {
22155  result = MA_FORMAT_NOT_SUPPORTED;
22156  }
22157  } else {
22158  /*
22159  I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself
22160  is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE
22161  want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be
22162  safe and only copy the WAVEFORMATEX part.
22163  */
22164  if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
22165  MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));
22166  } else {
22167  /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */
22168  size_t cbSize = pNativeFormat->cbSize;
22169  if (cbSize == 0) {
22170  cbSize = sizeof(MA_WAVEFORMATEX);
22171  }
22172 
22173  /* Make sure we don't copy more than the capacity of `wf`. */
22174  if (cbSize > sizeof(wf)) {
22175  cbSize = sizeof(wf);
22176  }
22177 
22178  MA_COPY_MEMORY(&wf, pNativeFormat, cbSize);
22179  }
22180 
22181  result = MA_SUCCESS;
22182  }
22183 
22184  ma_CoTaskMemFree(pContext, pNativeFormat);
22185 
22186  shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
22187  }
22188 
22189  /* Return an error if we still haven't found a format. */
22190  if (result != MA_SUCCESS) {
22191  errorMsg = "[WASAPI] Failed to find best device mix format.";
22192  goto done;
22193  }
22194 
22195  /*
22196  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
22197  WASAPI to perform the sample rate conversion.
22198  */
22199  nativeSampleRate = wf.nSamplesPerSec;
22200  if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
22201  wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE;
22202  wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
22203  }
22204 
22205  pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf);
22206  if (pData->formatOut == ma_format_unknown) {
22207  /*
22208  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
22209  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
22210  completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED.
22211  */
22212  if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
22213  result = MA_SHARE_MODE_NOT_SUPPORTED;
22214  } else {
22215  result = MA_FORMAT_NOT_SUPPORTED;
22216  }
22217 
22218  errorMsg = "[WASAPI] Native format not supported.";
22219  goto done;
22220  }
22221 
22222  pData->channelsOut = wf.nChannels;
22223  pData->sampleRateOut = wf.nSamplesPerSec;
22224 
22225  /*
22226  Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns
22227  a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this
22228  case we'll just use the default channel map.
22229  */
22230  if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) {
22231  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
22232  } else {
22233  ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
22234  }
22235 
22236  /* Period size. */
22237  pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS;
22238  pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
22239  if (pData->periodSizeInFramesOut == 0) {
22240  if (pData->periodSizeInMillisecondsIn == 0) {
22241  if (pData->performanceProfile == ma_performance_profile_low_latency) {
22242  pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec);
22243  } else {
22244  pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec);
22245  }
22246  } else {
22247  pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec);
22248  }
22249  }
22250 
22251  periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec;
22252 
22253 
22254  /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
22255  if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
22256  MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;
22257 
22258  /*
22259  If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
22260  it and trying it again.
22261  */
22262  hr = E_FAIL;
22263  for (;;) {
22264  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);
22265  if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
22266  if (bufferDuration > 500*10000) {
22267  break;
22268  } else {
22269  if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
22270  break;
22271  }
22272 
22273  bufferDuration = bufferDuration * 2;
22274  continue;
22275  }
22276  } else {
22277  break;
22278  }
22279  }
22280 
22281  if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
22282  ma_uint32 bufferSizeInFrames;
22283  hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
22284  if (SUCCEEDED(hr)) {
22285  bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5);
22286 
22287  /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
22288  ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
22289 
22290  #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22291  hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
22292  #else
22293  hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
22294  #endif
22295 
22296  if (SUCCEEDED(hr)) {
22297  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);
22298  }
22299  }
22300  }
22301 
22302  if (FAILED(hr)) {
22303  /* 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. */
22304  if (hr == E_ACCESSDENIED) {
22305  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
22306  } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
22307  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
22308  } else {
22309  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
22310  }
22311  goto done;
22312  }
22313  }
22314 
22315  if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
22316  /*
22317  Low latency shared mode via IAudioClient3.
22318 
22319  NOTE
22320  ====
22321  Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
22322  use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
22323  any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
22324  that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
22325  */
22326  #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
22327  {
22328  if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) {
22329  ma_IAudioClient3* pAudioClient3 = NULL;
22330  hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
22331  if (SUCCEEDED(hr)) {
22332  ma_uint32 defaultPeriodInFrames;
22333  ma_uint32 fundamentalPeriodInFrames;
22334  ma_uint32 minPeriodInFrames;
22335  ma_uint32 maxPeriodInFrames;
22336  hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
22337  if (SUCCEEDED(hr)) {
22338  ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
22339  ma_uint32 actualPeriodInFrames = desiredPeriodInFrames;
22340 
22341  /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
22342  actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
22343  actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
22344 
22345  /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
22346  actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
22347 
22348  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
22349  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
22350  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
22351  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames);
22352  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames);
22353 
22354  /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
22355  if (actualPeriodInFrames >= desiredPeriodInFrames) {
22356  /*
22357  MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
22358  IAudioClient3_InitializeSharedAudioStream() will fail.
22359  */
22360  hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL);
22361  if (SUCCEEDED(hr)) {
22362  wasInitializedUsingIAudioClient3 = MA_TRUE;
22363  pData->periodSizeInFramesOut = actualPeriodInFrames;
22364 
22365  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n");
22366  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
22367  } else {
22368  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
22369  }
22370  } else {
22371  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");
22372  }
22373  } else {
22374  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
22375  }
22376 
22377  ma_IAudioClient3_Release(pAudioClient3);
22378  pAudioClient3 = NULL;
22379  }
22380  }
22381  }
22382  #else
22383  {
22384  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");
22385  }
22386  #endif
22387 
22388  /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
22389  if (!wasInitializedUsingIAudioClient3) {
22390  MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
22391  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL);
22392  if (FAILED(hr)) {
22393  if (hr == E_ACCESSDENIED) {
22394  errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
22395  } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
22396  errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
22397  } else {
22398  errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
22399  }
22400 
22401  goto done;
22402  }
22403  }
22404  }
22405 
22406  if (!wasInitializedUsingIAudioClient3) {
22407  ma_uint32 bufferSizeInFrames = 0;
22408  hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
22409  if (FAILED(hr)) {
22410  errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
22411  goto done;
22412  }
22413 
22414  /*
22415  When using process loopback mode, retrieval of the buffer size seems to result in totally
22416  incorrect values. In this case we'll just assume it's the same size as what we requested
22417  when we initialized the client.
22418  */
22419  if (usingProcessLoopback) {
22420  bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000);
22421  }
22422 
22423  pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
22424  }
22425 
22426  pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
22427 
22428 
22429  if (deviceType == ma_device_type_playback) {
22430  result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
22431  } else {
22432  result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient);
22433  }
22434 
22435  /*if (FAILED(hr)) {*/
22436  if (result != MA_SUCCESS) {
22437  errorMsg = "[WASAPI] Failed to get audio client service.";
22438  goto done;
22439  }
22440 
22441 
22442  /* Grab the name of the device. */
22443  #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22444  {
22445  ma_IPropertyStore *pProperties;
22446  hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
22447  if (SUCCEEDED(hr)) {
22448  MA_PROPVARIANT varName;
22449  ma_PropVariantInit(&varName);
22450  hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
22451  if (SUCCEEDED(hr)) {
22452  WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
22453  ma_PropVariantClear(pContext, &varName);
22454  }
22455 
22456  ma_IPropertyStore_Release(pProperties);
22457  }
22458  }
22459  #endif
22460 
22461  /*
22462  For the WASAPI backend we need to know the actual IDs of the device in order to do automatic
22463  stream routing so that IDs can be compared and we can determine which device has been detached
22464  and whether or not it matches with our ma_device.
22465  */
22466  #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22467  {
22468  /* Desktop */
22469  ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id);
22470  }
22471  #else
22472  {
22473  /* UWP */
22474  /* TODO: Implement me. Need to figure out how to get the ID of the default device. */
22475  }
22476  #endif
22477 
22478 done:
22479  /* Clean up. */
22480 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22481  if (pDeviceInterface != NULL) {
22482  ma_IMMDevice_Release(pDeviceInterface);
22483  }
22484 #else
22485  if (pDeviceInterface != NULL) {
22486  ma_IUnknown_Release(pDeviceInterface);
22487  }
22488 #endif
22489 
22490  if (result != MA_SUCCESS) {
22491  if (pData->pRenderClient) {
22492  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
22493  pData->pRenderClient = NULL;
22494  }
22495  if (pData->pCaptureClient) {
22496  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
22497  pData->pCaptureClient = NULL;
22498  }
22499  if (pData->pAudioClient) {
22500  ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
22501  pData->pAudioClient = NULL;
22502  }
22503 
22504  if (errorMsg != NULL && errorMsg[0] != '\0') {
22505  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg);
22506  }
22507 
22508  return result;
22509  } else {
22510  return MA_SUCCESS;
22511  }
22512 }
22513 
22514 static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
22515 {
22516  ma_device_init_internal_data__wasapi data;
22517  ma_result result;
22518 
22519  MA_ASSERT(pDevice != NULL);
22520 
22521  /* We only re-initialize the playback or capture device. Never a full-duplex device. */
22522  if (deviceType == ma_device_type_duplex) {
22523  return MA_INVALID_ARGS;
22524  }
22525 
22526 
22527  /*
22528  Before reinitializing the device we need to free the previous audio clients.
22529 
22530  There's a known memory leak here. We will be calling this from the routing change callback that
22531  is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion
22532  this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably
22533  need some system where we post an event, but delay the execution of it until the callback has
22534  returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for
22535  a command thread which might be useful for this.
22536  */
22537  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
22538  if (pDevice->wasapi.pCaptureClient) {
22539  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
22540  pDevice->wasapi.pCaptureClient = NULL;
22541  }
22542 
22543  if (pDevice->wasapi.pAudioClientCapture) {
22544  /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/
22545  pDevice->wasapi.pAudioClientCapture = NULL;
22546  }
22547  }
22548 
22549  if (deviceType == ma_device_type_playback) {
22550  if (pDevice->wasapi.pRenderClient) {
22551  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
22552  pDevice->wasapi.pRenderClient = NULL;
22553  }
22554 
22555  if (pDevice->wasapi.pAudioClientPlayback) {
22556  /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/
22557  pDevice->wasapi.pAudioClientPlayback = NULL;
22558  }
22559  }
22560 
22561 
22562  if (deviceType == ma_device_type_playback) {
22563  data.formatIn = pDevice->playback.format;
22564  data.channelsIn = pDevice->playback.channels;
22565  MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
22566  data.shareMode = pDevice->playback.shareMode;
22567  } else {
22568  data.formatIn = pDevice->capture.format;
22569  data.channelsIn = pDevice->capture.channels;
22570  MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
22571  data.shareMode = pDevice->capture.shareMode;
22572  }
22573 
22574  data.sampleRateIn = pDevice->sampleRate;
22575  data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
22576  data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
22577  data.periodsIn = pDevice->wasapi.originalPeriods;
22578  data.performanceProfile = pDevice->wasapi.originalPerformanceProfile;
22579  data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
22580  data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
22581  data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
22582  data.loopbackProcessID = pDevice->wasapi.loopbackProcessID;
22583  data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude;
22584  result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
22585  if (result != MA_SUCCESS) {
22586  return result;
22587  }
22588 
22589  /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
22590  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
22591  pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
22592  pDevice->wasapi.pCaptureClient = data.pCaptureClient;
22593 
22594  pDevice->capture.internalFormat = data.formatOut;
22595  pDevice->capture.internalChannels = data.channelsOut;
22596  pDevice->capture.internalSampleRate = data.sampleRateOut;
22597  MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
22598  pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
22599  pDevice->capture.internalPeriods = data.periodsOut;
22600  ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
22601 
22602  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);
22603 
22604  pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
22605  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
22606 
22607  /* We must always have a valid ID. */
22608  ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
22609  }
22610 
22611  if (deviceType == ma_device_type_playback) {
22612  pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
22613  pDevice->wasapi.pRenderClient = data.pRenderClient;
22614 
22615  pDevice->playback.internalFormat = data.formatOut;
22616  pDevice->playback.internalChannels = data.channelsOut;
22617  pDevice->playback.internalSampleRate = data.sampleRateOut;
22618  MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
22619  pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
22620  pDevice->playback.internalPeriods = data.periodsOut;
22621  ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
22622 
22623  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);
22624 
22625  pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
22626  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
22627 
22628  /* We must always have a valid ID because rerouting will look at it. */
22629  ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
22630  }
22631 
22632  return MA_SUCCESS;
22633 }
22634 
22635 static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
22636 {
22637  ma_result result = MA_SUCCESS;
22638 
22639 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22640  HRESULT hr;
22641  ma_IMMDeviceEnumerator* pDeviceEnumerator;
22642 #endif
22643 
22644  MA_ASSERT(pDevice != NULL);
22645 
22646  MA_ZERO_OBJECT(&pDevice->wasapi);
22647  pDevice->wasapi.usage = pConfig->wasapi.usage;
22648  pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
22649  pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
22650  pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
22651  pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
22652  pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
22653 
22654  /* Exclusive mode is not allowed with loopback. */
22655  if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {
22656  return MA_INVALID_DEVICE_CONFIG;
22657  }
22658 
22659  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
22660  ma_device_init_internal_data__wasapi data;
22661  data.formatIn = pDescriptorCapture->format;
22662  data.channelsIn = pDescriptorCapture->channels;
22663  data.sampleRateIn = pDescriptorCapture->sampleRate;
22664  MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
22665  data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
22666  data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
22667  data.periodsIn = pDescriptorCapture->periodCount;
22668  data.shareMode = pDescriptorCapture->shareMode;
22669  data.performanceProfile = pConfig->performanceProfile;
22670  data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
22671  data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
22672  data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
22673  data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
22674  data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
22675 
22676  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);
22677  if (result != MA_SUCCESS) {
22678  return result;
22679  }
22680 
22681  pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
22682  pDevice->wasapi.pCaptureClient = data.pCaptureClient;
22683  pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
22684  pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
22685  pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount;
22686  pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
22687 
22688  /*
22689  The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
22690  however, because we want to block until we actually have something for the first call to ma_device_read().
22691  */
22692  pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
22693  if (pDevice->wasapi.hEventCapture == NULL) {
22694  result = ma_result_from_GetLastError(GetLastError());
22695 
22696  if (pDevice->wasapi.pCaptureClient != NULL) {
22697  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
22698  pDevice->wasapi.pCaptureClient = NULL;
22699  }
22700  if (pDevice->wasapi.pAudioClientCapture != NULL) {
22701  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22702  pDevice->wasapi.pAudioClientCapture = NULL;
22703  }
22704 
22705  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.");
22706  return result;
22707  }
22708  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);
22709 
22710  pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
22711  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
22712 
22713  /* We must always have a valid ID. */
22714  ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
22715 
22716  /* The descriptor needs to be updated with actual values. */
22717  pDescriptorCapture->format = data.formatOut;
22718  pDescriptorCapture->channels = data.channelsOut;
22719  pDescriptorCapture->sampleRate = data.sampleRateOut;
22720  MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
22721  pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
22722  pDescriptorCapture->periodCount = data.periodsOut;
22723  }
22724 
22725  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22726  ma_device_init_internal_data__wasapi data;
22727  data.formatIn = pDescriptorPlayback->format;
22728  data.channelsIn = pDescriptorPlayback->channels;
22729  data.sampleRateIn = pDescriptorPlayback->sampleRate;
22730  MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
22731  data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
22732  data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
22733  data.periodsIn = pDescriptorPlayback->periodCount;
22734  data.shareMode = pDescriptorPlayback->shareMode;
22735  data.performanceProfile = pConfig->performanceProfile;
22736  data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
22737  data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
22738  data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
22739  data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
22740  data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
22741 
22742  result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
22743  if (result != MA_SUCCESS) {
22744  if (pConfig->deviceType == ma_device_type_duplex) {
22745  if (pDevice->wasapi.pCaptureClient != NULL) {
22746  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
22747  pDevice->wasapi.pCaptureClient = NULL;
22748  }
22749  if (pDevice->wasapi.pAudioClientCapture != NULL) {
22750  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22751  pDevice->wasapi.pAudioClientCapture = NULL;
22752  }
22753 
22754  CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
22755  pDevice->wasapi.hEventCapture = NULL;
22756  }
22757  return result;
22758  }
22759 
22760  pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
22761  pDevice->wasapi.pRenderClient = data.pRenderClient;
22762  pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
22763  pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
22764  pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount;
22765  pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
22766 
22767  /*
22768  The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
22769  only after the whole available space has been filled, never before.
22770 
22771  The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
22772  to get passed WaitForMultipleObjects().
22773  */
22774  pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
22775  if (pDevice->wasapi.hEventPlayback == NULL) {
22776  result = ma_result_from_GetLastError(GetLastError());
22777 
22778  if (pConfig->deviceType == ma_device_type_duplex) {
22779  if (pDevice->wasapi.pCaptureClient != NULL) {
22780  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
22781  pDevice->wasapi.pCaptureClient = NULL;
22782  }
22783  if (pDevice->wasapi.pAudioClientCapture != NULL) {
22784  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22785  pDevice->wasapi.pAudioClientCapture = NULL;
22786  }
22787 
22788  CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
22789  pDevice->wasapi.hEventCapture = NULL;
22790  }
22791 
22792  if (pDevice->wasapi.pRenderClient != NULL) {
22793  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
22794  pDevice->wasapi.pRenderClient = NULL;
22795  }
22796  if (pDevice->wasapi.pAudioClientPlayback != NULL) {
22797  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
22798  pDevice->wasapi.pAudioClientPlayback = NULL;
22799  }
22800 
22801  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.");
22802  return result;
22803  }
22804  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);
22805 
22806  pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
22807  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
22808 
22809  /* We must always have a valid ID because rerouting will look at it. */
22810  ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
22811 
22812  /* The descriptor needs to be updated with actual values. */
22813  pDescriptorPlayback->format = data.formatOut;
22814  pDescriptorPlayback->channels = data.channelsOut;
22815  pDescriptorPlayback->sampleRate = data.sampleRateOut;
22816  MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
22817  pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
22818  pDescriptorPlayback->periodCount = data.periodsOut;
22819  }
22820 
22821  /*
22822  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
22823  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
22824  stop the device outright and let the application handle it.
22825  */
22826 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22827  if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
22828  if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) {
22829  pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
22830  }
22831  if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
22832  pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
22833  }
22834  }
22835 
22836  ma_mutex_init(&pDevice->wasapi.rerouteLock);
22837 
22838  hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
22839  if (FAILED(hr)) {
22840  ma_device_uninit__wasapi(pDevice);
22841  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
22842  return ma_result_from_HRESULT(hr);
22843  }
22844 
22845  pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
22846  pDevice->wasapi.notificationClient.counter = 1;
22847  pDevice->wasapi.notificationClient.pDevice = pDevice;
22848 
22849  hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
22850  if (SUCCEEDED(hr)) {
22851  pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
22852  } else {
22853  /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
22854  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
22855  }
22856 #endif
22857 
22858  ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
22859  ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
22860 
22861  return MA_SUCCESS;
22862 }
22863 
22864 static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
22865 {
22866  ma_uint32 paddingFramesCount;
22867  HRESULT hr;
22868  ma_share_mode shareMode;
22869 
22870  MA_ASSERT(pDevice != NULL);
22871  MA_ASSERT(pFrameCount != NULL);
22872 
22873  *pFrameCount = 0;
22874 
22875  if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
22876  return MA_INVALID_OPERATION;
22877  }
22878 
22879  /*
22880  I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing
22881  higher level function calls from doing anything because it thinks nothing is available. I have
22882  taken a look at the documentation and it looks like this is unnecessary in exclusive mode.
22883 
22884  From Microsoft's documentation:
22885 
22886  For an exclusive-mode rendering or capture stream that was initialized with the
22887  AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding
22888  value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during
22889  each processing pass.
22890 
22891  Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the
22892  entire buffer. This depends on the caller making sure they wait on the event handler.
22893  */
22894  shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
22895  if (shareMode == ma_share_mode_shared) {
22896  /* Shared mode. */
22897  hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
22898  if (FAILED(hr)) {
22899  return ma_result_from_HRESULT(hr);
22900  }
22901 
22902  if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
22903  *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount;
22904  } else {
22905  *pFrameCount = paddingFramesCount;
22906  }
22907  } else {
22908  /* Exclusive mode. */
22909  if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
22910  *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback;
22911  } else {
22912  *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture;
22913  }
22914  }
22915 
22916  return MA_SUCCESS;
22917 }
22918 
22919 
22920 static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
22921 {
22922  ma_result result;
22923 
22924  if (deviceType == ma_device_type_duplex) {
22925  return MA_INVALID_ARGS;
22926  }
22927 
22928  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n");
22929 
22930  result = ma_device_reinit__wasapi(pDevice, deviceType);
22931  if (result != MA_SUCCESS) {
22932  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n");
22933  return result;
22934  }
22935 
22936  ma_device__post_init_setup(pDevice, deviceType);
22937  ma_device__on_notification_rerouted(pDevice);
22938 
22939  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n");
22940 
22941  return MA_SUCCESS;
22942 }
22943 
22944 static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice)
22945 {
22946  HRESULT hr;
22947 
22948  if (pDevice->pContext->wasapi.hAvrt) {
22949  const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage);
22950  if (pTaskName) {
22951  DWORD idx = 0;
22952  pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx);
22953  }
22954  }
22955 
22956  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
22957  hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22958  if (FAILED(hr)) {
22959  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr);
22960  return ma_result_from_HRESULT(hr);
22961  }
22962 
22963  ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE);
22964  }
22965 
22966  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22967  hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
22968  if (FAILED(hr)) {
22969  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr);
22970  return ma_result_from_HRESULT(hr);
22971  }
22972 
22973  ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
22974  }
22975 
22976  return MA_SUCCESS;
22977 }
22978 
22979 static ma_result ma_device_start__wasapi(ma_device* pDevice)
22980 {
22981  ma_result result;
22982 
22983  MA_ASSERT(pDevice != NULL);
22984 
22985  /* Wait for any rerouting to finish before attempting to start the device. */
22986  ma_mutex_lock(&pDevice->wasapi.rerouteLock);
22987  {
22988  result = ma_device_start__wasapi_nolock(pDevice);
22989  }
22990  ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
22991 
22992  return result;
22993 }
22994 
22995 static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
22996 {
22997  ma_result result;
22998  HRESULT hr;
22999 
23000  MA_ASSERT(pDevice != NULL);
23001 
23002  if (pDevice->wasapi.hAvrtHandle) {
23003  ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle);
23004  pDevice->wasapi.hAvrtHandle = NULL;
23005  }
23006 
23007  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
23008  hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
23009  if (FAILED(hr)) {
23010  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.");
23011  return ma_result_from_HRESULT(hr);
23012  }
23013 
23014  /* The audio client needs to be reset otherwise restarting will fail. */
23015  hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
23016  if (FAILED(hr)) {
23017  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.");
23018  return ma_result_from_HRESULT(hr);
23019  }
23020 
23021  /* If we have a mapped buffer we need to release it. */
23022  if (pDevice->wasapi.pMappedBufferCapture != NULL) {
23023  ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
23024  pDevice->wasapi.pMappedBufferCapture = NULL;
23025  pDevice->wasapi.mappedBufferCaptureCap = 0;
23026  pDevice->wasapi.mappedBufferCaptureLen = 0;
23027  }
23028 
23029  ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
23030  }
23031 
23032  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23033  /*
23034  The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
23035  the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
23036  */
23037  if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) {
23038  /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
23039  DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate;
23040 
23041  if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
23042  WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
23043  }
23044  else {
23045  ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
23046  ma_uint32 framesAvailablePlayback;
23047  for (;;) {
23048  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
23049  if (result != MA_SUCCESS) {
23050  break;
23051  }
23052 
23053  if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) {
23054  break;
23055  }
23056 
23057  /*
23058  Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
23059  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.
23060  */
23061  if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
23062  break;
23063  }
23064  prevFramesAvaialablePlayback = framesAvailablePlayback;
23065 
23066  WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000);
23067  ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */
23068  }
23069  }
23070  }
23071 
23072  hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
23073  if (FAILED(hr)) {
23074  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.");
23075  return ma_result_from_HRESULT(hr);
23076  }
23077 
23078  /* The audio client needs to be reset otherwise restarting will fail. */
23079  hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
23080  if (FAILED(hr)) {
23081  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.");
23082  return ma_result_from_HRESULT(hr);
23083  }
23084 
23085  if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
23086  ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
23087  pDevice->wasapi.pMappedBufferPlayback = NULL;
23088  pDevice->wasapi.mappedBufferPlaybackCap = 0;
23089  pDevice->wasapi.mappedBufferPlaybackLen = 0;
23090  }
23091 
23092  ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
23093  }
23094 
23095  return MA_SUCCESS;
23096 }
23097 
23098 static ma_result ma_device_stop__wasapi(ma_device* pDevice)
23099 {
23100  ma_result result;
23101 
23102  MA_ASSERT(pDevice != NULL);
23103 
23104  /* Wait for any rerouting to finish before attempting to stop the device. */
23105  ma_mutex_lock(&pDevice->wasapi.rerouteLock);
23106  {
23107  result = ma_device_stop__wasapi_nolock(pDevice);
23108  }
23109  ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
23110 
23111  return result;
23112 }
23113 
23114 
23115 #ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS
23116 #define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000
23117 #endif
23118 
23119 static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
23120 {
23121  ma_result result = MA_SUCCESS;
23122  ma_uint32 totalFramesProcessed = 0;
23123 
23124  /*
23125  When reading, we need to get a buffer and process all of it before releasing it. Because the
23126  frame count (frameCount) can be different to the size of the buffer, we'll need to cache the
23127  pointer to the buffer.
23128  */
23129 
23130  /* Keep running until we've processed the requested number of frames. */
23131  while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
23132  ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
23133 
23134  /* If we have a mapped data buffer, consume that first. */
23135  if (pDevice->wasapi.pMappedBufferCapture != NULL) {
23136  /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */
23137  ma_uint32 framesToProcessNow = framesRemaining;
23138  if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) {
23139  framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen;
23140  }
23141 
23142  /* Now just copy the data over to the output buffer. */
23143  ma_copy_pcm_frames(
23144  ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
23145  ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
23146  framesToProcessNow,
23147  pDevice->capture.internalFormat, pDevice->capture.internalChannels
23148  );
23149 
23150  totalFramesProcessed += framesToProcessNow;
23151  pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow;
23152 
23153  /* If the data buffer has been fully consumed we need to release it. */
23154  if (pDevice->wasapi.mappedBufferCaptureLen == 0) {
23155  ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
23156  pDevice->wasapi.pMappedBufferCapture = NULL;
23157  pDevice->wasapi.mappedBufferCaptureCap = 0;
23158  }
23159  } else {
23160  /* We don't have any cached data pointer, so grab another one. */
23161  HRESULT hr;
23162  DWORD flags = 0;
23163 
23164  /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */
23165  hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
23166  if (hr == S_OK) {
23167  /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */
23168  pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
23169 
23170  /*
23171  There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every
23172  call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially
23173  work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution
23174  would be to figure out why the flag is always getting reported.
23175  */
23176  #if defined(MA_DEBUG_OUTPUT)
23177  {
23178  if (flags != 0) {
23179  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags);
23180 
23181  if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
23182  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);
23183  }
23184  }
23185  }
23186  #endif
23187 
23188  /* Overrun detection. */
23189  if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
23190  /* Glitched. Probably due to an overrun. */
23191 
23192  /*
23193  If we got an overrun it probably means we're straddling the end of the buffer. In normal capture
23194  mode this is the fault of the client application because they're responsible for ensuring data is
23195  processed fast enough. In duplex mode, however, the processing of audio is tied to the playback
23196  device, so this can possibly be the result of a timing de-sync.
23197 
23198  In capture mode we're not going to do any kind of recovery because the real fix is for the client
23199  application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers
23200  to prevent a never-ending sequence of glitches due to straddling the end of the buffer.
23201  */
23202  if (pDevice->type == ma_device_type_duplex) {
23203  /*
23204  Experiment:
23205 
23206  If we empty out the *entire* buffer we may end up putting ourselves into an underrun position
23207  which isn't really any better than the overrun we're probably in right now. Instead we'll just
23208  empty out about half.
23209  */
23210  ma_uint32 i;
23211  ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture);
23212  ma_uint32 iterationCount = periodCount / 2;
23213  if ((periodCount % 2) > 0) {
23214  iterationCount += 1;
23215  }
23216 
23217  for (i = 0; i < iterationCount; i += 1) {
23218  hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
23219  if (FAILED(hr)) {
23220  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr);
23221  break;
23222  }
23223 
23224  flags = 0;
23225  hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
23226  if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) {
23227  /*
23228  The buffer has been completely emptied or an error occurred. In this case we'll need
23229  to reset the state of the mapped buffer which will trigger the next iteration to get
23230  a fresh buffer from WASAPI.
23231  */
23232  pDevice->wasapi.pMappedBufferCapture = NULL;
23233  pDevice->wasapi.mappedBufferCaptureCap = 0;
23234  pDevice->wasapi.mappedBufferCaptureLen = 0;
23235 
23236  if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) {
23237  if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
23238  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n");
23239  } else {
23240  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n");
23241  }
23242  }
23243 
23244  if (FAILED(hr)) {
23245  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr);
23246  }
23247 
23248  break;
23249  }
23250  }
23251 
23252  /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */
23253  if (pDevice->wasapi.pMappedBufferCapture != NULL) {
23254  pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
23255  }
23256  }
23257  }
23258 
23259  continue;
23260  } else {
23261  if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
23262  /*
23263  No data is available. We need to wait for more. There's two situations to consider
23264  here. The first is normal capture mode. If this times out it probably means the
23265  microphone isn't delivering data for whatever reason. In this case we'll just
23266  abort the read and return whatever we were able to get. The other situations is
23267  loopback mode, in which case a timeout probably just means the nothing is playing
23268  through the speakers.
23269  */
23270 
23271  /* Experiment: Use a shorter timeout for loopback mode. */
23272  DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS;
23273  if (pDevice->type == ma_device_type_loopback) {
23274  timeoutInMilliseconds = 10;
23275  }
23276 
23277  if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) {
23278  if (pDevice->type == ma_device_type_loopback) {
23279  continue; /* Keep waiting in loopback mode. */
23280  } else {
23281  result = MA_ERROR;
23282  break; /* Wait failed. */
23283  }
23284  }
23285 
23286  /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */
23287  } else {
23288  /* An error occurred and we need to abort. */
23289  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);
23290  result = ma_result_from_HRESULT(hr);
23291  break;
23292  }
23293  }
23294  }
23295  }
23296 
23297  /*
23298  If we were unable to process the entire requested frame count, but we still have a mapped buffer,
23299  there's a good chance either an error occurred or the device was stopped mid-read. In this case
23300  we'll need to make sure the buffer is released.
23301  */
23302  if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) {
23303  ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
23304  pDevice->wasapi.pMappedBufferCapture = NULL;
23305  pDevice->wasapi.mappedBufferCaptureCap = 0;
23306  pDevice->wasapi.mappedBufferCaptureLen = 0;
23307  }
23308 
23309  if (pFramesRead != NULL) {
23310  *pFramesRead = totalFramesProcessed;
23311  }
23312 
23313  return result;
23314 }
23315 
23316 static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
23317 {
23318  ma_result result = MA_SUCCESS;
23319  ma_uint32 totalFramesProcessed = 0;
23320 
23321  /* Keep writing to the device until it's stopped or we've consumed all of our input. */
23322  while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
23323  ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
23324 
23325  /*
23326  We're going to do this in a similar way to capture. We'll first check if the cached data pointer
23327  is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with
23328  a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE
23329  it means we need to wait for some data to become available.
23330  */
23331  if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
23332  /* We still have some space available in the mapped data buffer. Write to it. */
23333  ma_uint32 framesToProcessNow = framesRemaining;
23334  if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) {
23335  framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen);
23336  }
23337 
23338  /* Now just copy the data over to the output buffer. */
23339  ma_copy_pcm_frames(
23340  ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
23341  ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
23342  framesToProcessNow,
23343  pDevice->playback.internalFormat, pDevice->playback.internalChannels
23344  );
23345 
23346  totalFramesProcessed += framesToProcessNow;
23347  pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow;
23348 
23349  /* If the data buffer has been fully consumed we need to release it. */
23350  if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) {
23351  ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
23352  pDevice->wasapi.pMappedBufferPlayback = NULL;
23353  pDevice->wasapi.mappedBufferPlaybackCap = 0;
23354  pDevice->wasapi.mappedBufferPlaybackLen = 0;
23355 
23356  /*
23357  In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never
23358  seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine
23359  whether or not we need to wait for more data.
23360  */
23361  if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
23362  if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
23363  result = MA_ERROR;
23364  break; /* Wait failed. Probably timed out. */
23365  }
23366  }
23367  }
23368  } else {
23369  /* We don't have a mapped data buffer so we'll need to get one. */
23370  HRESULT hr;
23371  ma_uint32 bufferSizeInFrames;
23372 
23373  /* Special rules for exclusive mode. */
23374  if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
23375  bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback;
23376  } else {
23377  bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback;
23378  }
23379 
23380  hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback);
23381  if (hr == S_OK) {
23382  /* We have data available. */
23383  pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames;
23384  pDevice->wasapi.mappedBufferPlaybackLen = 0;
23385  } else {
23386  if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
23387  /* Not enough data available. We need to wait for more. */
23388  if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
23389  result = MA_ERROR;
23390  break; /* Wait failed. Probably timed out. */
23391  }
23392  } else {
23393  /* Some error occurred. We'll need to abort. */
23394  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);
23395  result = ma_result_from_HRESULT(hr);
23396  break;
23397  }
23398  }
23399  }
23400  }
23401 
23402  if (pFramesWritten != NULL) {
23403  *pFramesWritten = totalFramesProcessed;
23404  }
23405 
23406  return result;
23407 }
23408 
23409 static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)
23410 {
23411  MA_ASSERT(pDevice != NULL);
23412 
23413  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
23414  SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
23415  }
23416 
23417  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23418  SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
23419  }
23420 
23421  return MA_SUCCESS;
23422 }
23423 
23424 
23425 static ma_result ma_context_uninit__wasapi(ma_context* pContext)
23426 {
23427  ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
23428 
23429  MA_ASSERT(pContext != NULL);
23430  MA_ASSERT(pContext->backend == ma_backend_wasapi);
23431 
23432  ma_context_post_command__wasapi(pContext, &cmd);
23433  ma_thread_wait(&pContext->wasapi.commandThread);
23434 
23435  if (pContext->wasapi.hAvrt) {
23436  ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt);
23437  pContext->wasapi.hAvrt = NULL;
23438  }
23439 
23440  #if defined(MA_WIN32_UWP)
23441  {
23442  if (pContext->wasapi.hMMDevapi) {
23443  ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi);
23444  pContext->wasapi.hMMDevapi = NULL;
23445  }
23446  }
23447  #endif
23448 
23449  /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */
23450  ma_semaphore_uninit(&pContext->wasapi.commandSem);
23451  ma_mutex_uninit(&pContext->wasapi.commandLock);
23452 
23453  return MA_SUCCESS;
23454 }
23455 
23456 static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
23457 {
23458  ma_result result = MA_SUCCESS;
23459 
23460  MA_ASSERT(pContext != NULL);
23461 
23462  (void)pConfig;
23463 
23464 #ifdef MA_WIN32_DESKTOP
23465  /*
23466  WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
23467  exclusive mode does not work until SP1.
23468 
23469  Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error.
23470  */
23471  {
23472  ma_OSVERSIONINFOEXW osvi;
23473  ma_handle kernel32DLL;
23474  ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
23475  ma_PFNVerSetConditionMask _VerSetConditionMask;
23476 
23477  kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll");
23478  if (kernel32DLL == NULL) {
23479  return MA_NO_BACKEND;
23480  }
23481 
23482  _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW");
23483  _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask");
23484  if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
23485  ma_dlclose(ma_context_get_log(pContext), kernel32DLL);
23486  return MA_NO_BACKEND;
23487  }
23488 
23489  MA_ZERO_OBJECT(&osvi);
23490  osvi.dwOSVersionInfoSize = sizeof(osvi);
23491  osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
23492  osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
23493  osvi.wServicePackMajor = 1;
23494  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))) {
23495  result = MA_SUCCESS;
23496  } else {
23497  result = MA_NO_BACKEND;
23498  }
23499 
23500  ma_dlclose(ma_context_get_log(pContext), kernel32DLL);
23501  }
23502 #endif
23503 
23504  if (result != MA_SUCCESS) {
23505  return result;
23506  }
23507 
23508  MA_ZERO_OBJECT(&pContext->wasapi);
23509 
23510 
23511  #if defined(MA_WIN32_UWP)
23512  {
23513  /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */
23514  pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll");
23515  if (pContext->wasapi.hMMDevapi) {
23516  pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync");
23517  if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) {
23518  ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi);
23519  return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */
23520  }
23521  } else {
23522  return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */
23523  }
23524  }
23525  #endif
23526 
23527  /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */
23528  pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll");
23529  if (pContext->wasapi.hAvrt) {
23530  pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA");
23531  pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics");
23532 
23533  /* If either function could not be found, disable use of avrt entirely. */
23534  if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) {
23535  pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL;
23536  pContext->wasapi.AvRevertMmThreadcharacteristics = NULL;
23537  ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt);
23538  pContext->wasapi.hAvrt = NULL;
23539  }
23540  }
23541 
23542 
23543  /*
23544  Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread
23545  than the one that retrieved it with GetService(). This can result in a deadlock in two
23546  situations:
23547 
23548  1) When calling ma_device_uninit() from a different thread to ma_device_init(); and
23549  2) When uninitializing and reinitializing the internal IAudioClient object in response to
23550  automatic stream routing.
23551 
23552  We could define ma_device_uninit() such that it must be called on the same thread as
23553  ma_device_init(). We could also just not release the IAudioClient when performing automatic
23554  stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so
23555  we're going to have to work around this with a worker thread. This is not ideal, but I can't
23556  think of a better way to do this.
23557 
23558  More information about this can be found here:
23559 
23560  https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient
23561 
23562  Note this section:
23563 
23564  When releasing an IAudioRenderClient interface instance, the client must call the interface's
23565  Release method from the same thread as the call to IAudioClient::GetService that created the
23566  object.
23567  */
23568  {
23569  result = ma_mutex_init(&pContext->wasapi.commandLock);
23570  if (result != MA_SUCCESS) {
23571  return result;
23572  }
23573 
23574  result = ma_semaphore_init(0, &pContext->wasapi.commandSem);
23575  if (result != MA_SUCCESS) {
23576  ma_mutex_uninit(&pContext->wasapi.commandLock);
23577  return result;
23578  }
23579 
23580  result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);
23581  if (result != MA_SUCCESS) {
23582  ma_semaphore_uninit(&pContext->wasapi.commandSem);
23583  ma_mutex_uninit(&pContext->wasapi.commandLock);
23584  return result;
23585  }
23586  }
23587 
23588 
23589  pCallbacks->onContextInit = ma_context_init__wasapi;
23590  pCallbacks->onContextUninit = ma_context_uninit__wasapi;
23591  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;
23592  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi;
23593  pCallbacks->onDeviceInit = ma_device_init__wasapi;
23594  pCallbacks->onDeviceUninit = ma_device_uninit__wasapi;
23595  pCallbacks->onDeviceStart = ma_device_start__wasapi;
23596  pCallbacks->onDeviceStop = ma_device_stop__wasapi;
23597  pCallbacks->onDeviceRead = ma_device_read__wasapi;
23598  pCallbacks->onDeviceWrite = ma_device_write__wasapi;
23599  pCallbacks->onDeviceDataLoop = NULL;
23600  pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi;
23601 
23602  return MA_SUCCESS;
23603 }
23604 #endif
23605 
23606 /******************************************************************************
23607 
23608 DirectSound Backend
23609 
23610 ******************************************************************************/
23611 #ifdef MA_HAS_DSOUND
23612 /*#include <dsound.h>*/
23613 
23614 /*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/
23615 
23616 /* miniaudio only uses priority or exclusive modes. */
23617 #define MA_DSSCL_NORMAL 1
23618 #define MA_DSSCL_PRIORITY 2
23619 #define MA_DSSCL_EXCLUSIVE 3
23620 #define MA_DSSCL_WRITEPRIMARY 4
23621 
23622 #define MA_DSCAPS_PRIMARYMONO 0x00000001
23623 #define MA_DSCAPS_PRIMARYSTEREO 0x00000002
23624 #define MA_DSCAPS_PRIMARY8BIT 0x00000004
23625 #define MA_DSCAPS_PRIMARY16BIT 0x00000008
23626 #define MA_DSCAPS_CONTINUOUSRATE 0x00000010
23627 #define MA_DSCAPS_EMULDRIVER 0x00000020
23628 #define MA_DSCAPS_CERTIFIED 0x00000040
23629 #define MA_DSCAPS_SECONDARYMONO 0x00000100
23630 #define MA_DSCAPS_SECONDARYSTEREO 0x00000200
23631 #define MA_DSCAPS_SECONDARY8BIT 0x00000400
23632 #define MA_DSCAPS_SECONDARY16BIT 0x00000800
23633 
23634 #define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
23635 #define MA_DSBCAPS_STATIC 0x00000002
23636 #define MA_DSBCAPS_LOCHARDWARE 0x00000004
23637 #define MA_DSBCAPS_LOCSOFTWARE 0x00000008
23638 #define MA_DSBCAPS_CTRL3D 0x00000010
23639 #define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
23640 #define MA_DSBCAPS_CTRLPAN 0x00000040
23641 #define MA_DSBCAPS_CTRLVOLUME 0x00000080
23642 #define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
23643 #define MA_DSBCAPS_CTRLFX 0x00000200
23644 #define MA_DSBCAPS_STICKYFOCUS 0x00004000
23645 #define MA_DSBCAPS_GLOBALFOCUS 0x00008000
23646 #define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
23647 #define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
23648 #define MA_DSBCAPS_LOCDEFER 0x00040000
23649 #define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
23650 
23651 #define MA_DSBPLAY_LOOPING 0x00000001
23652 #define MA_DSBPLAY_LOCHARDWARE 0x00000002
23653 #define MA_DSBPLAY_LOCSOFTWARE 0x00000004
23654 #define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
23655 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
23656 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
23657 
23658 #define MA_DSCBSTART_LOOPING 0x00000001
23659 
23660 typedef struct
23661 {
23662  DWORD dwSize;
23663  DWORD dwFlags;
23664  DWORD dwBufferBytes;
23665  DWORD dwReserved;
23666  MA_WAVEFORMATEX* lpwfxFormat;
23667  GUID guid3DAlgorithm;
23668 } MA_DSBUFFERDESC;
23669 
23670 typedef struct
23671 {
23672  DWORD dwSize;
23673  DWORD dwFlags;
23674  DWORD dwBufferBytes;
23675  DWORD dwReserved;
23676  MA_WAVEFORMATEX* lpwfxFormat;
23677  DWORD dwFXCount;
23678  void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
23679 } MA_DSCBUFFERDESC;
23680 
23681 typedef struct
23682 {
23683  DWORD dwSize;
23684  DWORD dwFlags;
23685  DWORD dwMinSecondarySampleRate;
23686  DWORD dwMaxSecondarySampleRate;
23687  DWORD dwPrimaryBuffers;
23688  DWORD dwMaxHwMixingAllBuffers;
23689  DWORD dwMaxHwMixingStaticBuffers;
23690  DWORD dwMaxHwMixingStreamingBuffers;
23691  DWORD dwFreeHwMixingAllBuffers;
23692  DWORD dwFreeHwMixingStaticBuffers;
23693  DWORD dwFreeHwMixingStreamingBuffers;
23694  DWORD dwMaxHw3DAllBuffers;
23695  DWORD dwMaxHw3DStaticBuffers;
23696  DWORD dwMaxHw3DStreamingBuffers;
23697  DWORD dwFreeHw3DAllBuffers;
23698  DWORD dwFreeHw3DStaticBuffers;
23699  DWORD dwFreeHw3DStreamingBuffers;
23700  DWORD dwTotalHwMemBytes;
23701  DWORD dwFreeHwMemBytes;
23702  DWORD dwMaxContigFreeHwMemBytes;
23703  DWORD dwUnlockTransferRateHwBuffers;
23704  DWORD dwPlayCpuOverheadSwBuffers;
23705  DWORD dwReserved1;
23706  DWORD dwReserved2;
23707 } MA_DSCAPS;
23708 
23709 typedef struct
23710 {
23711  DWORD dwSize;
23712  DWORD dwFlags;
23713  DWORD dwBufferBytes;
23714  DWORD dwUnlockTransferRate;
23715  DWORD dwPlayCpuOverhead;
23716 } MA_DSBCAPS;
23717 
23718 typedef struct
23719 {
23720  DWORD dwSize;
23721  DWORD dwFlags;
23722  DWORD dwFormats;
23723  DWORD dwChannels;
23724 } MA_DSCCAPS;
23725 
23726 typedef struct
23727 {
23728  DWORD dwSize;
23729  DWORD dwFlags;
23730  DWORD dwBufferBytes;
23731  DWORD dwReserved;
23732 } MA_DSCBCAPS;
23733 
23734 typedef struct
23735 {
23736  DWORD dwOffset;
23737  HANDLE hEventNotify;
23738 } MA_DSBPOSITIONNOTIFY;
23739 
23740 typedef struct ma_IDirectSound ma_IDirectSound;
23741 typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
23742 typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
23743 typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
23744 typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
23745 
23746 
23747 /*
23748 COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
23749 like how C++ works internally), and then you have a structure with a single member, which is a
23750 pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
23751 to be in a specific order, and parent classes need to have their methods declared first.
23752 */
23753 
23754 /* IDirectSound */
23755 typedef struct
23756 {
23757  /* IUnknown */
23758  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
23759  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
23760  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
23761 
23762  /* IDirectSound */
23763  HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
23764  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
23765  HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
23766  HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
23767  HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
23768  HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
23769  HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
23770  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
23771 } ma_IDirectSoundVtbl;
23772 struct ma_IDirectSound
23773 {
23774  ma_IDirectSoundVtbl* lpVtbl;
23775 };
23776 static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23777 static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23778 static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
23779 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); }
23780 static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
23781 static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
23782 static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
23783 static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
23784 static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
23785 static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
23786 static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
23787 
23788 
23789 /* IDirectSoundBuffer */
23790 typedef struct
23791 {
23792  /* IUnknown */
23793  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
23794  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
23795  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
23796 
23797  /* IDirectSoundBuffer */
23798  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
23799  HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
23800  HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
23801  HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
23802  HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
23803  HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
23804  HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
23805  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
23806  HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
23807  HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
23808  HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
23809  HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat);
23810  HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
23811  HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
23812  HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
23813  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
23814  HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
23815  HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
23816 } ma_IDirectSoundBufferVtbl;
23817 struct ma_IDirectSoundBuffer
23818 {
23819  ma_IDirectSoundBufferVtbl* lpVtbl;
23820 };
23821 static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23822 static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23823 static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
23824 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
23825 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
23826 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); }
23827 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
23828 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
23829 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
23830 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
23831 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
23832 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); }
23833 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
23834 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
23835 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
23836 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
23837 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
23838 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
23839 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
23840 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); }
23841 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
23842 
23843 
23844 /* IDirectSoundCapture */
23845 typedef struct
23846 {
23847  /* IUnknown */
23848  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
23849  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
23850  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
23851 
23852  /* IDirectSoundCapture */
23853  HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
23854  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
23855  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
23856 } ma_IDirectSoundCaptureVtbl;
23857 struct ma_IDirectSoundCapture
23858 {
23859  ma_IDirectSoundCaptureVtbl* lpVtbl;
23860 };
23861 static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23862 static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23863 static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
23864 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); }
23865 static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
23866 static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
23867 
23868 
23869 /* IDirectSoundCaptureBuffer */
23870 typedef struct
23871 {
23872  /* IUnknown */
23873  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
23874  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
23875  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
23876 
23877  /* IDirectSoundCaptureBuffer */
23878  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
23879  HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
23880  HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
23881  HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
23882  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
23883  HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
23884  HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
23885  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
23886  HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
23887 } ma_IDirectSoundCaptureBufferVtbl;
23888 struct ma_IDirectSoundCaptureBuffer
23889 {
23890  ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
23891 };
23892 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23893 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23894 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
23895 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
23896 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
23897 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); }
23898 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
23899 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
23900 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); }
23901 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
23902 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
23903 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); }
23904 
23905 
23906 /* IDirectSoundNotify */
23907 typedef struct
23908 {
23909  /* IUnknown */
23910  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
23911  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
23912  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
23913 
23914  /* IDirectSoundNotify */
23915  HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
23916 } ma_IDirectSoundNotifyVtbl;
23917 struct ma_IDirectSoundNotify
23918 {
23919  ma_IDirectSoundNotifyVtbl* lpVtbl;
23920 };
23921 static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23922 static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23923 static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
23924 static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
23925 
23926 
23927 typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext);
23928 typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter);
23929 typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);
23930 typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter);
23931 typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);
23932 
23933 static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
23934 {
23935  /* Normalize the range in case we were given something stupid. */
23936  if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) {
23937  sampleRateMin = (ma_uint32)ma_standard_sample_rate_min;
23938  }
23939  if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) {
23940  sampleRateMax = (ma_uint32)ma_standard_sample_rate_max;
23941  }
23942  if (sampleRateMin > sampleRateMax) {
23943  sampleRateMin = sampleRateMax;
23944  }
23945 
23946  if (sampleRateMin == sampleRateMax) {
23947  return sampleRateMax;
23948  } else {
23949  size_t iStandardRate;
23950  for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
23951  ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
23952  if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
23953  return standardRate;
23954  }
23955  }
23956  }
23957 
23958  /* Should never get here. */
23959  MA_ASSERT(MA_FALSE);
23960  return 0;
23961 }
23962 
23963 /*
23964 Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
23965 the channel count and channel map will be left unmodified.
23966 */
23967 static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
23968 {
23969  WORD channels;
23970  DWORD channelMap;
23971 
23972  channels = 0;
23973  if (pChannelsOut != NULL) {
23974  channels = *pChannelsOut;
23975  }
23976 
23977  channelMap = 0;
23978  if (pChannelMapOut != NULL) {
23979  channelMap = *pChannelMapOut;
23980  }
23981 
23982  /*
23983  The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
23984  16 bits is for the geometry.
23985  */
23986  switch ((BYTE)(speakerConfig)) {
23987  case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
23988  case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
23989  case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
23990  case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
23991  case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
23992  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;
23993  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;
23994  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;
23995  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;
23996  default: break;
23997  }
23998 
23999  if (pChannelsOut != NULL) {
24000  *pChannelsOut = channels;
24001  }
24002 
24003  if (pChannelMapOut != NULL) {
24004  *pChannelMapOut = channelMap;
24005  }
24006 }
24007 
24008 
24009 static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
24010 {
24011  ma_IDirectSound* pDirectSound;
24012  HWND hWnd;
24013  HRESULT hr;
24014 
24015  MA_ASSERT(pContext != NULL);
24016  MA_ASSERT(ppDirectSound != NULL);
24017 
24018  *ppDirectSound = NULL;
24019  pDirectSound = NULL;
24020 
24021  if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
24022  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.");
24023  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
24024  }
24025 
24026  /* The cooperative level must be set before doing anything else. */
24027  hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
24028  if (hWnd == 0) {
24029  hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
24030  }
24031 
24032  hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
24033  if (FAILED(hr)) {
24034  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.");
24035  return ma_result_from_HRESULT(hr);
24036  }
24037 
24038  *ppDirectSound = pDirectSound;
24039  return MA_SUCCESS;
24040 }
24041 
24042 static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
24043 {
24044  ma_IDirectSoundCapture* pDirectSoundCapture;
24045  HRESULT hr;
24046 
24047  MA_ASSERT(pContext != NULL);
24048  MA_ASSERT(ppDirectSoundCapture != NULL);
24049 
24050  /* DirectSound does not support exclusive mode for capture. */
24051  if (shareMode == ma_share_mode_exclusive) {
24052  return MA_SHARE_MODE_NOT_SUPPORTED;
24053  }
24054 
24055  *ppDirectSoundCapture = NULL;
24056  pDirectSoundCapture = NULL;
24057 
24058  hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);
24059  if (FAILED(hr)) {
24060  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.");
24061  return ma_result_from_HRESULT(hr);
24062  }
24063 
24064  *ppDirectSoundCapture = pDirectSoundCapture;
24065  return MA_SUCCESS;
24066 }
24067 
24068 static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
24069 {
24070  HRESULT hr;
24071  MA_DSCCAPS caps;
24072  WORD bitsPerSample;
24073  DWORD sampleRate;
24074 
24075  MA_ASSERT(pContext != NULL);
24076  MA_ASSERT(pDirectSoundCapture != NULL);
24077 
24078  if (pChannels) {
24079  *pChannels = 0;
24080  }
24081  if (pBitsPerSample) {
24082  *pBitsPerSample = 0;
24083  }
24084  if (pSampleRate) {
24085  *pSampleRate = 0;
24086  }
24087 
24088  MA_ZERO_OBJECT(&caps);
24089  caps.dwSize = sizeof(caps);
24090  hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);
24091  if (FAILED(hr)) {
24092  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.");
24093  return ma_result_from_HRESULT(hr);
24094  }
24095 
24096  if (pChannels) {
24097  *pChannels = (WORD)caps.dwChannels;
24098  }
24099 
24100  /* 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. */
24101  bitsPerSample = 16;
24102  sampleRate = 48000;
24103 
24104  if (caps.dwChannels == 1) {
24105  if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
24106  sampleRate = 48000;
24107  } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
24108  sampleRate = 44100;
24109  } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
24110  sampleRate = 22050;
24111  } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
24112  sampleRate = 11025;
24113  } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
24114  sampleRate = 96000;
24115  } else {
24116  bitsPerSample = 8;
24117  if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
24118  sampleRate = 48000;
24119  } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
24120  sampleRate = 44100;
24121  } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
24122  sampleRate = 22050;
24123  } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
24124  sampleRate = 11025;
24125  } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
24126  sampleRate = 96000;
24127  } else {
24128  bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
24129  }
24130  }
24131  } else if (caps.dwChannels == 2) {
24132  if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
24133  sampleRate = 48000;
24134  } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
24135  sampleRate = 44100;
24136  } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
24137  sampleRate = 22050;
24138  } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
24139  sampleRate = 11025;
24140  } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
24141  sampleRate = 96000;
24142  } else {
24143  bitsPerSample = 8;
24144  if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
24145  sampleRate = 48000;
24146  } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
24147  sampleRate = 44100;
24148  } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
24149  sampleRate = 22050;
24150  } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
24151  sampleRate = 11025;
24152  } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
24153  sampleRate = 96000;
24154  } else {
24155  bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
24156  }
24157  }
24158  }
24159 
24160  if (pBitsPerSample) {
24161  *pBitsPerSample = bitsPerSample;
24162  }
24163  if (pSampleRate) {
24164  *pSampleRate = sampleRate;
24165  }
24166 
24167  return MA_SUCCESS;
24168 }
24169 
24170 
24171 typedef struct
24172 {
24173  ma_context* pContext;
24174  ma_device_type deviceType;
24175  ma_enum_devices_callback_proc callback;
24176  void* pUserData;
24177  ma_bool32 terminated;
24178 } ma_context_enumerate_devices_callback_data__dsound;
24179 
24180 static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)
24181 {
24182  ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
24183  ma_device_info deviceInfo;
24184 
24185  (void)lpcstrModule;
24186 
24187  MA_ZERO_OBJECT(&deviceInfo);
24188 
24189  /* ID. */
24190  if (lpGuid != NULL) {
24191  MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
24192  } else {
24193  MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
24194  deviceInfo.isDefault = MA_TRUE;
24195  }
24196 
24197  /* Name / Description */
24198  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
24199 
24200 
24201  /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
24202  MA_ASSERT(pData != NULL);
24203  pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE);
24204  if (pData->terminated) {
24205  return FALSE; /* Stop enumeration. */
24206  } else {
24207  return TRUE; /* Continue enumeration. */
24208  }
24209 }
24210 
24211 static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
24212 {
24213  ma_context_enumerate_devices_callback_data__dsound data;
24214 
24215  MA_ASSERT(pContext != NULL);
24216  MA_ASSERT(callback != NULL);
24217 
24218  data.pContext = pContext;
24219  data.callback = callback;
24220  data.pUserData = pUserData;
24221  data.terminated = MA_FALSE;
24222 
24223  /* Playback. */
24224  if (!data.terminated) {
24225  data.deviceType = ma_device_type_playback;
24226  ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
24227  }
24228 
24229  /* Capture. */
24230  if (!data.terminated) {
24231  data.deviceType = ma_device_type_capture;
24232  ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
24233  }
24234 
24235  return MA_SUCCESS;
24236 }
24237 
24238 
24239 typedef struct
24240 {
24241  const ma_device_id* pDeviceID;
24242  ma_device_info* pDeviceInfo;
24243  ma_bool32 found;
24244 } ma_context_get_device_info_callback_data__dsound;
24245 
24246 static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)
24247 {
24248  ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
24249  MA_ASSERT(pData != NULL);
24250 
24251  if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {
24252  /* Default device. */
24253  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
24254  pData->pDeviceInfo->isDefault = MA_TRUE;
24255  pData->found = MA_TRUE;
24256  return FALSE; /* Stop enumeration. */
24257  } else {
24258  /* Not the default device. */
24259  if (lpGuid != NULL && pData->pDeviceID != NULL) {
24260  if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
24261  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
24262  pData->found = MA_TRUE;
24263  return FALSE; /* Stop enumeration. */
24264  }
24265  }
24266  }
24267 
24268  (void)lpcstrModule;
24269  return TRUE;
24270 }
24271 
24272 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)
24273 {
24274  ma_result result;
24275  HRESULT hr;
24276 
24277  if (pDeviceID != NULL) {
24278  ma_context_get_device_info_callback_data__dsound data;
24279 
24280  /* ID. */
24281  MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
24282 
24283  /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
24284  data.pDeviceID = pDeviceID;
24285  data.pDeviceInfo = pDeviceInfo;
24286  data.found = MA_FALSE;
24287  if (deviceType == ma_device_type_playback) {
24288  ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
24289  } else {
24290  ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
24291  }
24292 
24293  if (!data.found) {
24294  return MA_NO_DEVICE;
24295  }
24296  } else {
24297  /* 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. */
24298 
24299  /* ID */
24300  MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
24301 
24302  /* Name / Description */
24303  if (deviceType == ma_device_type_playback) {
24304  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24305  } else {
24306  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24307  }
24308 
24309  pDeviceInfo->isDefault = MA_TRUE;
24310  }
24311 
24312  /* Retrieving detailed information is slightly different depending on the device type. */
24313  if (deviceType == ma_device_type_playback) {
24314  /* Playback. */
24315  ma_IDirectSound* pDirectSound;
24316  MA_DSCAPS caps;
24317  WORD channels;
24318 
24319  result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound);
24320  if (result != MA_SUCCESS) {
24321  return result;
24322  }
24323 
24324  MA_ZERO_OBJECT(&caps);
24325  caps.dwSize = sizeof(caps);
24326  hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);
24327  if (FAILED(hr)) {
24328  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
24329  return ma_result_from_HRESULT(hr);
24330  }
24331 
24332 
24333  /* Channels. Only a single channel count is reported for DirectSound. */
24334  if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
24335  /* It supports at least stereo, but could support more. */
24336  DWORD speakerConfig;
24337 
24338  channels = 2;
24339 
24340  /* Look at the speaker configuration to get a better idea on the channel count. */
24341  hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);
24342  if (SUCCEEDED(hr)) {
24343  ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
24344  }
24345  } else {
24346  /* It does not support stereo, which means we are stuck with mono. */
24347  channels = 1;
24348  }
24349 
24350 
24351  /*
24352  In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel
24353  count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio
24354  in order to keep the size of this within reason.
24355  */
24356  if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
24357  /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */
24358  size_t iStandardSampleRate;
24359  for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
24360  ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
24361  if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) {
24362  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
24363  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
24364  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
24365  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
24366  pDeviceInfo->nativeDataFormatCount += 1;
24367  }
24368  }
24369  } else {
24370  /* Only a single sample rate is supported. */
24371  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
24372  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
24373  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate;
24374  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
24375  pDeviceInfo->nativeDataFormatCount += 1;
24376  }
24377 
24378  ma_IDirectSound_Release(pDirectSound);
24379  } else {
24380  /*
24381  Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
24382  devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
24383  reporting the best format.
24384  */
24385  ma_IDirectSoundCapture* pDirectSoundCapture;
24386  WORD channels;
24387  WORD bitsPerSample;
24388  DWORD sampleRate;
24389 
24390  result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture);
24391  if (result != MA_SUCCESS) {
24392  return result;
24393  }
24394 
24395  result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
24396  if (result != MA_SUCCESS) {
24397  ma_IDirectSoundCapture_Release(pDirectSoundCapture);
24398  return result;
24399  }
24400 
24401  ma_IDirectSoundCapture_Release(pDirectSoundCapture);
24402 
24403  /* The format is always an integer format and is based on the bits per sample. */
24404  if (bitsPerSample == 8) {
24405  pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
24406  } else if (bitsPerSample == 16) {
24407  pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
24408  } else if (bitsPerSample == 24) {
24409  pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
24410  } else if (bitsPerSample == 32) {
24411  pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
24412  } else {
24413  return MA_FORMAT_NOT_SUPPORTED;
24414  }
24415 
24416  pDeviceInfo->nativeDataFormats[0].channels = channels;
24417  pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
24418  pDeviceInfo->nativeDataFormats[0].flags = 0;
24419  pDeviceInfo->nativeDataFormatCount = 1;
24420  }
24421 
24422  return MA_SUCCESS;
24423 }
24424 
24425 
24426 
24427 static ma_result ma_device_uninit__dsound(ma_device* pDevice)
24428 {
24429  MA_ASSERT(pDevice != NULL);
24430 
24431  if (pDevice->dsound.pCaptureBuffer != NULL) {
24432  ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
24433  }
24434  if (pDevice->dsound.pCapture != NULL) {
24435  ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
24436  }
24437 
24438  if (pDevice->dsound.pPlaybackBuffer != NULL) {
24439  ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
24440  }
24441  if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
24442  ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
24443  }
24444  if (pDevice->dsound.pPlayback != NULL) {
24445  ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
24446  }
24447 
24448  return MA_SUCCESS;
24449 }
24450 
24451 static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF)
24452 {
24453  GUID subformat;
24454 
24455  if (format == ma_format_unknown) {
24456  format = MA_DEFAULT_FORMAT;
24457  }
24458 
24459  if (channels == 0) {
24460  channels = MA_DEFAULT_CHANNELS;
24461  }
24462 
24463  if (sampleRate == 0) {
24464  sampleRate = MA_DEFAULT_SAMPLE_RATE;
24465  }
24466 
24467  switch (format)
24468  {
24469  case ma_format_u8:
24470  case ma_format_s16:
24471  case ma_format_s24:
24472  /*case ma_format_s24_32:*/
24473  case ma_format_s32:
24474  {
24475  subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
24476  } break;
24477 
24478  case ma_format_f32:
24479  {
24480  subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
24481  } break;
24482 
24483  default:
24484  return MA_FORMAT_NOT_SUPPORTED;
24485  }
24486 
24487  MA_ZERO_OBJECT(pWF);
24488  pWF->cbSize = sizeof(*pWF);
24489  pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
24490  pWF->nChannels = (WORD)channels;
24491  pWF->nSamplesPerSec = (DWORD)sampleRate;
24492  pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
24493  pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
24494  pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
24495  pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample;
24496  pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
24497  pWF->SubFormat = subformat;
24498 
24499  return MA_SUCCESS;
24500 }
24501 
24502 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
24503 {
24504  /*
24505  DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for
24506  reliable glitch-free processing so going to use 30ms instead.
24507  */
24508  ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate);
24509  ma_uint32 periodSizeInFrames;
24510 
24511  periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
24512  if (periodSizeInFrames < minPeriodSizeInFrames) {
24513  periodSizeInFrames = minPeriodSizeInFrames;
24514  }
24515 
24516  return periodSizeInFrames;
24517 }
24518 
24519 static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
24520 {
24521  ma_result result;
24522  HRESULT hr;
24523 
24524  MA_ASSERT(pDevice != NULL);
24525 
24526  MA_ZERO_OBJECT(&pDevice->dsound);
24527 
24528  if (pConfig->deviceType == ma_device_type_loopback) {
24529  return MA_DEVICE_TYPE_NOT_SUPPORTED;
24530  }
24531 
24532  /*
24533  Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
24534  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
24535  full-duplex mode.
24536  */
24537  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
24538  MA_WAVEFORMATEXTENSIBLE wf;
24539  MA_DSCBUFFERDESC descDS;
24540  ma_uint32 periodSizeInFrames;
24541  ma_uint32 periodCount;
24542  char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
24543  MA_WAVEFORMATEXTENSIBLE* pActualFormat;
24544 
24545  result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf);
24546  if (result != MA_SUCCESS) {
24547  return result;
24548  }
24549 
24550  result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
24551  if (result != MA_SUCCESS) {
24552  ma_device_uninit__dsound(pDevice);
24553  return result;
24554  }
24555 
24556  result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec);
24557  if (result != MA_SUCCESS) {
24558  ma_device_uninit__dsound(pDevice);
24559  return result;
24560  }
24561 
24562  wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
24563  wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
24564  wf.Samples.wValidBitsPerSample = wf.wBitsPerSample;
24565  wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
24566 
24567  /* The size of the buffer must be a clean multiple of the period count. */
24568  periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile);
24569  periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS;
24570 
24571  MA_ZERO_OBJECT(&descDS);
24572  descDS.dwSize = sizeof(descDS);
24573  descDS.dwFlags = 0;
24574  descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign;
24575  descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf;
24576  hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
24577  if (FAILED(hr)) {
24578  ma_device_uninit__dsound(pDevice);
24579  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
24580  return ma_result_from_HRESULT(hr);
24581  }
24582 
24583  /* Get the _actual_ properties of the buffer. */
24584  pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;
24585  hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
24586  if (FAILED(hr)) {
24587  ma_device_uninit__dsound(pDevice);
24588  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.");
24589  return ma_result_from_HRESULT(hr);
24590  }
24591 
24592  /* We can now start setting the output data formats. */
24593  pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);
24594  pDescriptorCapture->channels = pActualFormat->nChannels;
24595  pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec;
24596 
24597  /* Get the native channel map based on the channel mask. */
24598  if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
24599  ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
24600  } else {
24601  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
24602  }
24603 
24604  /*
24605  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
24606  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.
24607  */
24608  if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) {
24609  descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount;
24610  ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
24611 
24612  hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
24613  if (FAILED(hr)) {
24614  ma_device_uninit__dsound(pDevice);
24615  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
24616  return ma_result_from_HRESULT(hr);
24617  }
24618  }
24619 
24620  /* DirectSound should give us a buffer exactly the size we asked for. */
24621  pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
24622  pDescriptorCapture->periodCount = periodCount;
24623  }
24624 
24625  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24626  MA_WAVEFORMATEXTENSIBLE wf;
24627  MA_DSBUFFERDESC descDSPrimary;
24628  MA_DSCAPS caps;
24629  char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
24630  MA_WAVEFORMATEXTENSIBLE* pActualFormat;
24631  ma_uint32 periodSizeInFrames;
24632  ma_uint32 periodCount;
24633  MA_DSBUFFERDESC descDS;
24634  WORD nativeChannelCount;
24635  DWORD nativeChannelMask = 0;
24636 
24637  result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf);
24638  if (result != MA_SUCCESS) {
24639  return result;
24640  }
24641 
24642  result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
24643  if (result != MA_SUCCESS) {
24644  ma_device_uninit__dsound(pDevice);
24645  return result;
24646  }
24647 
24648  MA_ZERO_OBJECT(&descDSPrimary);
24649  descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
24650  descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
24651  hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);
24652  if (FAILED(hr)) {
24653  ma_device_uninit__dsound(pDevice);
24654  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.");
24655  return ma_result_from_HRESULT(hr);
24656  }
24657 
24658 
24659  /* We may want to make some adjustments to the format if we are using defaults. */
24660  MA_ZERO_OBJECT(&caps);
24661  caps.dwSize = sizeof(caps);
24662  hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);
24663  if (FAILED(hr)) {
24664  ma_device_uninit__dsound(pDevice);
24665  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
24666  return ma_result_from_HRESULT(hr);
24667  }
24668 
24669  if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
24670  DWORD speakerConfig;
24671 
24672  /* It supports at least stereo, but could support more. */
24673  nativeChannelCount = 2;
24674 
24675  /* Look at the speaker configuration to get a better idea on the channel count. */
24676  if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
24677  ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask);
24678  }
24679  } else {
24680  /* It does not support stereo, which means we are stuck with mono. */
24681  nativeChannelCount = 1;
24682  nativeChannelMask = 0x00000001;
24683  }
24684 
24685  if (pDescriptorPlayback->channels == 0) {
24686  wf.nChannels = nativeChannelCount;
24687  wf.dwChannelMask = nativeChannelMask;
24688  }
24689 
24690  if (pDescriptorPlayback->sampleRate == 0) {
24691  /* We base the sample rate on the values returned by GetCaps(). */
24692  if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
24693  wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
24694  } else {
24695  wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
24696  }
24697  }
24698 
24699  wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
24700  wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
24701 
24702  /*
24703  From MSDN:
24704 
24705  The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
24706  supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
24707  and compare the result with the format that was requested with the SetFormat method.
24708  */
24709  hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);
24710  if (FAILED(hr)) {
24711  /*
24712  If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have
24713  observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a
24714  sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will
24715  use 44100 for the sample rate.
24716  */
24717  wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */
24718  wf.wFormatTag = WAVE_FORMAT_PCM;
24719  wf.wBitsPerSample = 16;
24720  wf.nChannels = nativeChannelCount;
24721  wf.nSamplesPerSec = 44100;
24722  wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8);
24723  wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
24724 
24725  hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);
24726  if (FAILED(hr)) {
24727  ma_device_uninit__dsound(pDevice);
24728  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.");
24729  return ma_result_from_HRESULT(hr);
24730  }
24731  }
24732 
24733  /* Get the _actual_ properties of the buffer. */
24734  pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;
24735  hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
24736  if (FAILED(hr)) {
24737  ma_device_uninit__dsound(pDevice);
24738  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.");
24739  return ma_result_from_HRESULT(hr);
24740  }
24741 
24742  /* We now have enough information to start setting some output properties. */
24743  pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);
24744  pDescriptorPlayback->channels = pActualFormat->nChannels;
24745  pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec;
24746 
24747  /* Get the internal channel map based on the channel mask. */
24748  if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
24749  ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
24750  } else {
24751  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
24752  }
24753 
24754  /* The size of the buffer must be a clean multiple of the period count. */
24755  periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
24756  periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS;
24757 
24758  /*
24759  Meaning of dwFlags (from MSDN):
24760 
24761  DSBCAPS_CTRLPOSITIONNOTIFY
24762  The buffer has position notification capability.
24763 
24764  DSBCAPS_GLOBALFOCUS
24765  With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
24766  another application, even if the new application uses DirectSound.
24767 
24768  DSBCAPS_GETCURRENTPOSITION2
24769  In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
24770  sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
24771  application can get a more accurate play cursor.
24772  */
24773  MA_ZERO_OBJECT(&descDS);
24774  descDS.dwSize = sizeof(descDS);
24775  descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
24776  descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);
24777  descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat;
24778  hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);
24779  if (FAILED(hr)) {
24780  ma_device_uninit__dsound(pDevice);
24781  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.");
24782  return ma_result_from_HRESULT(hr);
24783  }
24784 
24785  /* DirectSound should give us a buffer exactly the size we asked for. */
24786  pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
24787  pDescriptorPlayback->periodCount = periodCount;
24788  }
24789 
24790  return MA_SUCCESS;
24791 }
24792 
24793 
24794 static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
24795 {
24796  ma_result result = MA_SUCCESS;
24797  ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24798  ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24799  HRESULT hr;
24800  DWORD lockOffsetInBytesCapture;
24801  DWORD lockSizeInBytesCapture;
24802  DWORD mappedSizeInBytesCapture;
24803  DWORD mappedDeviceFramesProcessedCapture;
24804  void* pMappedDeviceBufferCapture;
24805  DWORD lockOffsetInBytesPlayback;
24806  DWORD lockSizeInBytesPlayback;
24807  DWORD mappedSizeInBytesPlayback;
24808  void* pMappedDeviceBufferPlayback;
24809  DWORD prevReadCursorInBytesCapture = 0;
24810  DWORD prevPlayCursorInBytesPlayback = 0;
24811  ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
24812  DWORD virtualWriteCursorInBytesPlayback = 0;
24813  ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
24814  ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
24815  ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
24816  ma_uint32 waitTimeInMilliseconds = 1;
24817 
24818  MA_ASSERT(pDevice != NULL);
24819 
24820  /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
24821  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24822  hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING);
24823  if (FAILED(hr)) {
24824  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.");
24825  return ma_result_from_HRESULT(hr);
24826  }
24827  }
24828 
24829  while (ma_device_get_state(pDevice) == ma_device_state_started) {
24830  switch (pDevice->type)
24831  {
24832  case ma_device_type_duplex:
24833  {
24834  DWORD physicalCaptureCursorInBytes;
24835  DWORD physicalReadCursorInBytes;
24836  hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
24837  if (FAILED(hr)) {
24838  return ma_result_from_HRESULT(hr);
24839  }
24840 
24841  /* If nothing is available we just sleep for a bit and return from this iteration. */
24842  if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
24843  ma_sleep(waitTimeInMilliseconds);
24844  continue; /* Nothing is available in the capture buffer. */
24845  }
24846 
24847  /*
24848  The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
24849  we don't return until every frame has been copied over.
24850  */
24851  if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
24852  /* The capture position has not looped. This is the simple case. */
24853  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
24854  lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
24855  } else {
24856  /*
24857  The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
24858  do it again from the start.
24859  */
24860  if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
24861  /* Lock up to the end of the buffer. */
24862  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
24863  lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
24864  } else {
24865  /* Lock starting from the start of the buffer. */
24866  lockOffsetInBytesCapture = 0;
24867  lockSizeInBytesCapture = physicalReadCursorInBytes;
24868  }
24869  }
24870 
24871  if (lockSizeInBytesCapture == 0) {
24872  ma_sleep(waitTimeInMilliseconds);
24873  continue; /* Nothing is available in the capture buffer. */
24874  }
24875 
24876  hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
24877  if (FAILED(hr)) {
24878  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.");
24879  return ma_result_from_HRESULT(hr);
24880  }
24881 
24882 
24883  /* 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. */
24884  mappedDeviceFramesProcessedCapture = 0;
24885 
24886  for (;;) { /* Keep writing to the playback device. */
24887  ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
24888  ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
24889  ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
24890  ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
24891  ma_uint32 outputFramesInClientFormatCount;
24892  ma_uint32 outputFramesInClientFormatConsumed = 0;
24893  ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
24894  ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
24895  void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
24896 
24897  result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
24898  if (result != MA_SUCCESS) {
24899  break;
24900  }
24901 
24902  outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
24903  mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
24904 
24905  ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
24906 
24907  /* 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. */
24908  for (;;) {
24909  ma_uint32 framesWrittenThisIteration;
24910  DWORD physicalPlayCursorInBytes;
24911  DWORD physicalWriteCursorInBytes;
24912  DWORD availableBytesPlayback;
24913  DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
24914 
24915  /* We need the physical play and write cursors. */
24916  if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
24917  break;
24918  }
24919 
24920  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
24921  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
24922  }
24923  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
24924 
24925  /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
24926  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
24927  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
24928  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
24929  availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
24930  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
24931  } else {
24932  /* This is an error. */
24933  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);
24934  availableBytesPlayback = 0;
24935  }
24936  } else {
24937  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
24938  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
24939  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
24940  } else {
24941  /* This is an error. */
24942  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);
24943  availableBytesPlayback = 0;
24944  }
24945  }
24946 
24947  /* If there's no room available for writing we need to wait for more. */
24948  if (availableBytesPlayback == 0) {
24949  /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
24950  if (!isPlaybackDeviceStarted) {
24951  hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
24952  if (FAILED(hr)) {
24953  ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
24954  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
24955  return ma_result_from_HRESULT(hr);
24956  }
24957  isPlaybackDeviceStarted = MA_TRUE;
24958  } else {
24959  ma_sleep(waitTimeInMilliseconds);
24960  continue;
24961  }
24962  }
24963 
24964 
24965  /* 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. */
24966  lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
24967  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
24968  /* Same loop iteration. Go up to the end of the buffer. */
24969  lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
24970  } else {
24971  /* Different loop iterations. Go up to the physical play cursor. */
24972  lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
24973  }
24974 
24975  hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
24976  if (FAILED(hr)) {
24977  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.");
24978  result = ma_result_from_HRESULT(hr);
24979  break;
24980  }
24981 
24982  /*
24983  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
24984  endless glitching due to it constantly running out of data.
24985  */
24986  if (isPlaybackDeviceStarted) {
24987  DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
24988  if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
24989  silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
24990  if (silentPaddingInBytes > lockSizeInBytesPlayback) {
24991  silentPaddingInBytes = lockSizeInBytesPlayback;
24992  }
24993 
24994  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);
24995  }
24996  }
24997 
24998  /* At this point we have a buffer for output. */
24999  if (silentPaddingInBytes > 0) {
25000  MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
25001  framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
25002  } else {
25003  ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
25004  ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
25005  void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
25006  void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
25007 
25008  result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
25009  if (result != MA_SUCCESS) {
25010  break;
25011  }
25012 
25013  outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
25014  framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
25015  }
25016 
25017 
25018  hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
25019  if (FAILED(hr)) {
25020  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.");
25021  result = ma_result_from_HRESULT(hr);
25022  break;
25023  }
25024 
25025  virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
25026  if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
25027  virtualWriteCursorInBytesPlayback = 0;
25028  virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
25029  }
25030 
25031  /*
25032  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
25033  a bit of a buffer to prevent the playback buffer from getting starved.
25034  */
25035  framesWrittenToPlaybackDevice += framesWrittenThisIteration;
25036  if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
25037  hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
25038  if (FAILED(hr)) {
25039  ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
25040  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
25041  return ma_result_from_HRESULT(hr);
25042  }
25043  isPlaybackDeviceStarted = MA_TRUE;
25044  }
25045 
25046  if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
25047  break; /* We're finished with the output data.*/
25048  }
25049  }
25050 
25051  if (clientCapturedFramesToProcess == 0) {
25052  break; /* We just consumed every input sample. */
25053  }
25054  }
25055 
25056 
25057  /* At this point we're done with the mapped portion of the capture buffer. */
25058  hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
25059  if (FAILED(hr)) {
25060  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.");
25061  return ma_result_from_HRESULT(hr);
25062  }
25063  prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
25064  } break;
25065 
25066 
25067 
25068  case ma_device_type_capture:
25069  {
25070  DWORD physicalCaptureCursorInBytes;
25071  DWORD physicalReadCursorInBytes;
25072  hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
25073  if (FAILED(hr)) {
25074  return MA_ERROR;
25075  }
25076 
25077  /* If the previous capture position is the same as the current position we need to wait a bit longer. */
25078  if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
25079  ma_sleep(waitTimeInMilliseconds);
25080  continue;
25081  }
25082 
25083  /* Getting here means we have capture data available. */
25084  if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
25085  /* The capture position has not looped. This is the simple case. */
25086  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
25087  lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
25088  } else {
25089  /*
25090  The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
25091  do it again from the start.
25092  */
25093  if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
25094  /* Lock up to the end of the buffer. */
25095  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
25096  lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
25097  } else {
25098  /* Lock starting from the start of the buffer. */
25099  lockOffsetInBytesCapture = 0;
25100  lockSizeInBytesCapture = physicalReadCursorInBytes;
25101  }
25102  }
25103 
25104  if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
25105  ma_sleep(waitTimeInMilliseconds);
25106  continue; /* Nothing is available in the capture buffer. */
25107  }
25108 
25109  hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
25110  if (FAILED(hr)) {
25111  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.");
25112  result = ma_result_from_HRESULT(hr);
25113  }
25114 
25115  if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
25116  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
25117  }
25118 
25119  ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
25120 
25121  hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
25122  if (FAILED(hr)) {
25123  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.");
25124  return ma_result_from_HRESULT(hr);
25125  }
25126  prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
25127 
25128  if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
25129  prevReadCursorInBytesCapture = 0;
25130  }
25131  } break;
25132 
25133 
25134 
25135  case ma_device_type_playback:
25136  {
25137  DWORD availableBytesPlayback;
25138  DWORD physicalPlayCursorInBytes;
25139  DWORD physicalWriteCursorInBytes;
25140  hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
25141  if (FAILED(hr)) {
25142  break;
25143  }
25144 
25145  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
25146  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
25147  }
25148  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
25149 
25150  /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
25151  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
25152  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
25153  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
25154  availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
25155  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
25156  } else {
25157  /* This is an error. */
25158  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);
25159  availableBytesPlayback = 0;
25160  }
25161  } else {
25162  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
25163  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
25164  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
25165  } else {
25166  /* This is an error. */
25167  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);
25168  availableBytesPlayback = 0;
25169  }
25170  }
25171 
25172  /* If there's no room available for writing we need to wait for more. */
25173  if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
25174  /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
25175  if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
25176  hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
25177  if (FAILED(hr)) {
25178  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
25179  return ma_result_from_HRESULT(hr);
25180  }
25181  isPlaybackDeviceStarted = MA_TRUE;
25182  } else {
25183  ma_sleep(waitTimeInMilliseconds);
25184  continue;
25185  }
25186  }
25187 
25188  /* 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. */
25189  lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
25190  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
25191  /* Same loop iteration. Go up to the end of the buffer. */
25192  lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
25193  } else {
25194  /* Different loop iterations. Go up to the physical play cursor. */
25195  lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
25196  }
25197 
25198  hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
25199  if (FAILED(hr)) {
25200  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.");
25201  result = ma_result_from_HRESULT(hr);
25202  break;
25203  }
25204 
25205  /* At this point we have a buffer for output. */
25206  ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
25207 
25208  hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
25209  if (FAILED(hr)) {
25210  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.");
25211  result = ma_result_from_HRESULT(hr);
25212  break;
25213  }
25214 
25215  virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
25216  if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
25217  virtualWriteCursorInBytesPlayback = 0;
25218  virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
25219  }
25220 
25221  /*
25222  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
25223  a bit of a buffer to prevent the playback buffer from getting starved.
25224  */
25225  framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
25226  if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
25227  hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
25228  if (FAILED(hr)) {
25229  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
25230  return ma_result_from_HRESULT(hr);
25231  }
25232  isPlaybackDeviceStarted = MA_TRUE;
25233  }
25234  } break;
25235 
25236 
25237  default: return MA_INVALID_ARGS; /* Invalid device type. */
25238  }
25239 
25240  if (result != MA_SUCCESS) {
25241  return result;
25242  }
25243  }
25244 
25245  /* Getting here means the device is being stopped. */
25246  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25247  hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
25248  if (FAILED(hr)) {
25249  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.");
25250  return ma_result_from_HRESULT(hr);
25251  }
25252  }
25253 
25254  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25255  /* 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. */
25256  if (isPlaybackDeviceStarted) {
25257  for (;;) {
25258  DWORD availableBytesPlayback = 0;
25259  DWORD physicalPlayCursorInBytes;
25260  DWORD physicalWriteCursorInBytes;
25261  hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
25262  if (FAILED(hr)) {
25263  break;
25264  }
25265 
25266  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
25267  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
25268  }
25269  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
25270 
25271  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
25272  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
25273  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
25274  availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
25275  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
25276  } else {
25277  break;
25278  }
25279  } else {
25280  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
25281  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
25282  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
25283  } else {
25284  break;
25285  }
25286  }
25287 
25288  if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
25289  break;
25290  }
25291 
25292  ma_sleep(waitTimeInMilliseconds);
25293  }
25294  }
25295 
25296  hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
25297  if (FAILED(hr)) {
25298  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.");
25299  return ma_result_from_HRESULT(hr);
25300  }
25301 
25302  ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
25303  }
25304 
25305  return MA_SUCCESS;
25306 }
25307 
25308 static ma_result ma_context_uninit__dsound(ma_context* pContext)
25309 {
25310  MA_ASSERT(pContext != NULL);
25311  MA_ASSERT(pContext->backend == ma_backend_dsound);
25312 
25313  ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL);
25314 
25315  return MA_SUCCESS;
25316 }
25317 
25318 static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
25319 {
25320  MA_ASSERT(pContext != NULL);
25321 
25322  (void)pConfig;
25323 
25324  pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll");
25325  if (pContext->dsound.hDSoundDLL == NULL) {
25326  return MA_API_NOT_FOUND;
25327  }
25328 
25329  pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate");
25330  pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
25331  pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
25332  pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
25333 
25334  /*
25335  We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too
25336  well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient
25337  place to just disable the DirectSound backend for Windows 95.
25338  */
25339  if (pContext->dsound.DirectSoundCreate == NULL ||
25340  pContext->dsound.DirectSoundEnumerateA == NULL ||
25341  pContext->dsound.DirectSoundCaptureCreate == NULL ||
25342  pContext->dsound.DirectSoundCaptureEnumerateA == NULL) {
25343  return MA_API_NOT_FOUND;
25344  }
25345 
25346  pCallbacks->onContextInit = ma_context_init__dsound;
25347  pCallbacks->onContextUninit = ma_context_uninit__dsound;
25348  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
25349  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound;
25350  pCallbacks->onDeviceInit = ma_device_init__dsound;
25351  pCallbacks->onDeviceUninit = ma_device_uninit__dsound;
25352  pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */
25353  pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */
25354  pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */
25355  pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */
25356  pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound;
25357 
25358  return MA_SUCCESS;
25359 }
25360 #endif
25361 
25362 
25363 
25364 /******************************************************************************
25365 
25366 WinMM Backend
25367 
25368 ******************************************************************************/
25369 #ifdef MA_HAS_WINMM
25370 
25371 /*
25372 Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN
25373 is defined. We need to define the types and functions we need manually.
25374 */
25375 #define MA_MMSYSERR_NOERROR 0
25376 #define MA_MMSYSERR_ERROR 1
25377 #define MA_MMSYSERR_BADDEVICEID 2
25378 #define MA_MMSYSERR_INVALHANDLE 5
25379 #define MA_MMSYSERR_NOMEM 7
25380 #define MA_MMSYSERR_INVALFLAG 10
25381 #define MA_MMSYSERR_INVALPARAM 11
25382 #define MA_MMSYSERR_HANDLEBUSY 12
25383 
25384 #define MA_CALLBACK_EVENT 0x00050000
25385 #define MA_WAVE_ALLOWSYNC 0x0002
25386 
25387 #define MA_WHDR_DONE 0x00000001
25388 #define MA_WHDR_PREPARED 0x00000002
25389 #define MA_WHDR_BEGINLOOP 0x00000004
25390 #define MA_WHDR_ENDLOOP 0x00000008
25391 #define MA_WHDR_INQUEUE 0x00000010
25392 
25393 #define MA_MAXPNAMELEN 32
25394 
25395 typedef void* MA_HWAVEIN;
25396 typedef void* MA_HWAVEOUT;
25397 typedef UINT MA_MMRESULT;
25398 typedef UINT MA_MMVERSION;
25399 
25400 typedef struct
25401 {
25402  WORD wMid;
25403  WORD wPid;
25404  MA_MMVERSION vDriverVersion;
25405  CHAR szPname[MA_MAXPNAMELEN];
25406  DWORD dwFormats;
25407  WORD wChannels;
25408  WORD wReserved1;
25409 } MA_WAVEINCAPSA;
25410 
25411 typedef struct
25412 {
25413  WORD wMid;
25414  WORD wPid;
25415  MA_MMVERSION vDriverVersion;
25416  CHAR szPname[MA_MAXPNAMELEN];
25417  DWORD dwFormats;
25418  WORD wChannels;
25419  WORD wReserved1;
25420  DWORD dwSupport;
25421 } MA_WAVEOUTCAPSA;
25422 
25423 typedef struct tagWAVEHDR
25424 {
25425  char* lpData;
25426  DWORD dwBufferLength;
25427  DWORD dwBytesRecorded;
25428  DWORD_PTR dwUser;
25429  DWORD dwFlags;
25430  DWORD dwLoops;
25431  struct tagWAVEHDR* lpNext;
25432  DWORD_PTR reserved;
25433 } MA_WAVEHDR;
25434 
25435 typedef struct
25436 {
25437  WORD wMid;
25438  WORD wPid;
25439  MA_MMVERSION vDriverVersion;
25440  CHAR szPname[MA_MAXPNAMELEN];
25441  DWORD dwFormats;
25442  WORD wChannels;
25443  WORD wReserved1;
25444  DWORD dwSupport;
25445  GUID ManufacturerGuid;
25446  GUID ProductGuid;
25447  GUID NameGuid;
25448 } MA_WAVEOUTCAPS2A;
25449 
25450 typedef struct
25451 {
25452  WORD wMid;
25453  WORD wPid;
25454  MA_MMVERSION vDriverVersion;
25455  CHAR szPname[MA_MAXPNAMELEN];
25456  DWORD dwFormats;
25457  WORD wChannels;
25458  WORD wReserved1;
25459  GUID ManufacturerGuid;
25460  GUID ProductGuid;
25461  GUID NameGuid;
25462 } MA_WAVEINCAPS2A;
25463 
25464 typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
25465 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc);
25466 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
25467 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo);
25468 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
25469 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
25470 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
25471 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo);
25472 typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
25473 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic);
25474 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
25475 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi);
25476 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
25477 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
25478 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
25479 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi);
25480 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi);
25481 
25482 static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM)
25483 {
25484  switch (resultMM)
25485  {
25486  case MA_MMSYSERR_NOERROR: return MA_SUCCESS;
25487  case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
25488  case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
25489  case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
25490  case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
25491  case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
25492  case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY;
25493  case MA_MMSYSERR_ERROR: return MA_ERROR;
25494  default: return MA_ERROR;
25495  }
25496 }
25497 
25498 static char* ma_find_last_character(char* str, char ch)
25499 {
25500  char* last;
25501 
25502  if (str == NULL) {
25503  return NULL;
25504  }
25505 
25506  last = NULL;
25507  while (*str != '\0') {
25508  if (*str == ch) {
25509  last = str;
25510  }
25511 
25512  str += 1;
25513  }
25514 
25515  return last;
25516 }
25517 
25518 static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
25519 {
25520  return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
25521 }
25522 
25523 
25524 /*
25525 Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
25526 we can do things generically and typesafely. Names are being kept the same for consistency.
25527 */
25528 typedef struct
25529 {
25530  CHAR szPname[MA_MAXPNAMELEN];
25531  DWORD dwFormats;
25532  WORD wChannels;
25533  GUID NameGuid;
25534 } MA_WAVECAPSA;
25535 
25536 static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
25537 {
25538  WORD bitsPerSample = 0;
25539  DWORD sampleRate = 0;
25540 
25541  if (pBitsPerSample) {
25542  *pBitsPerSample = 0;
25543  }
25544  if (pSampleRate) {
25545  *pSampleRate = 0;
25546  }
25547 
25548  if (channels == 1) {
25549  bitsPerSample = 16;
25550  if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
25551  sampleRate = 48000;
25552  } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
25553  sampleRate = 44100;
25554  } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
25555  sampleRate = 22050;
25556  } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
25557  sampleRate = 11025;
25558  } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
25559  sampleRate = 96000;
25560  } else {
25561  bitsPerSample = 8;
25562  if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
25563  sampleRate = 48000;
25564  } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
25565  sampleRate = 44100;
25566  } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
25567  sampleRate = 22050;
25568  } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
25569  sampleRate = 11025;
25570  } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
25571  sampleRate = 96000;
25572  } else {
25573  return MA_FORMAT_NOT_SUPPORTED;
25574  }
25575  }
25576  } else {
25577  bitsPerSample = 16;
25578  if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
25579  sampleRate = 48000;
25580  } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
25581  sampleRate = 44100;
25582  } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
25583  sampleRate = 22050;
25584  } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
25585  sampleRate = 11025;
25586  } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
25587  sampleRate = 96000;
25588  } else {
25589  bitsPerSample = 8;
25590  if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
25591  sampleRate = 48000;
25592  } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
25593  sampleRate = 44100;
25594  } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
25595  sampleRate = 22050;
25596  } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
25597  sampleRate = 11025;
25598  } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
25599  sampleRate = 96000;
25600  } else {
25601  return MA_FORMAT_NOT_SUPPORTED;
25602  }
25603  }
25604  }
25605 
25606  if (pBitsPerSample) {
25607  *pBitsPerSample = bitsPerSample;
25608  }
25609  if (pSampleRate) {
25610  *pSampleRate = sampleRate;
25611  }
25612 
25613  return MA_SUCCESS;
25614 }
25615 
25616 static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF)
25617 {
25618  ma_result result;
25619 
25620  MA_ASSERT(pWF != NULL);
25621 
25622  MA_ZERO_OBJECT(pWF);
25623  pWF->cbSize = sizeof(*pWF);
25624  pWF->wFormatTag = WAVE_FORMAT_PCM;
25625  pWF->nChannels = (WORD)channels;
25626  if (pWF->nChannels > 2) {
25627  pWF->nChannels = 2;
25628  }
25629 
25630  result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec);
25631  if (result != MA_SUCCESS) {
25632  return result;
25633  }
25634 
25635  pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
25636  pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
25637 
25638  return MA_SUCCESS;
25639 }
25640 
25641 static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
25642 {
25643  WORD bitsPerSample;
25644  DWORD sampleRate;
25645  ma_result result;
25646 
25647  MA_ASSERT(pContext != NULL);
25648  MA_ASSERT(pCaps != NULL);
25649  MA_ASSERT(pDeviceInfo != NULL);
25650 
25651  /*
25652  Name / Description
25653 
25654  Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
25655  situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
25656  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.
25657  */
25658 
25659  /* Set the default to begin with. */
25660  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
25661 
25662  /*
25663  Now try the registry. There's a few things to consider here:
25664  - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
25665  - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
25666  - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
25667  problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
25668  but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
25669  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
25670  name, and then concatenate the name from the registry.
25671  */
25672  if (!ma_is_guid_null(&pCaps->NameGuid)) {
25673  WCHAR guidStrW[256];
25674  if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
25675  char guidStr[256];
25676  char keyStr[1024];
25677  HKEY hKey;
25678 
25679  WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
25680 
25681  ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
25682  ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
25683 
25684  if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
25685  BYTE nameFromReg[512];
25686  DWORD nameFromRegSize = sizeof(nameFromReg);
25687  LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize);
25688  ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
25689 
25690  if (resultWin32 == ERROR_SUCCESS) {
25691  /* We have the value from the registry, so now we need to construct the name string. */
25692  char name[1024];
25693  if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
25694  char* nameBeg = ma_find_last_character(name, '(');
25695  if (nameBeg != NULL) {
25696  size_t leadingLen = (nameBeg - name);
25697  ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
25698 
25699  /* The closing ")", if it can fit. */
25700  if (leadingLen + nameFromRegSize < sizeof(name)-1) {
25701  ma_strcat_s(name, sizeof(name), ")");
25702  }
25703 
25704  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
25705  }
25706  }
25707  }
25708  }
25709  }
25710  }
25711 
25712 
25713  result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
25714  if (result != MA_SUCCESS) {
25715  return result;
25716  }
25717 
25718  if (bitsPerSample == 8) {
25719  pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
25720  } else if (bitsPerSample == 16) {
25721  pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
25722  } else if (bitsPerSample == 24) {
25723  pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
25724  } else if (bitsPerSample == 32) {
25725  pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
25726  } else {
25727  return MA_FORMAT_NOT_SUPPORTED;
25728  }
25729  pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels;
25730  pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
25731  pDeviceInfo->nativeDataFormats[0].flags = 0;
25732  pDeviceInfo->nativeDataFormatCount = 1;
25733 
25734  return MA_SUCCESS;
25735 }
25736 
25737 static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
25738 {
25739  MA_WAVECAPSA caps;
25740 
25741  MA_ASSERT(pContext != NULL);
25742  MA_ASSERT(pCaps != NULL);
25743  MA_ASSERT(pDeviceInfo != NULL);
25744 
25745  MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
25746  caps.dwFormats = pCaps->dwFormats;
25747  caps.wChannels = pCaps->wChannels;
25748  caps.NameGuid = pCaps->NameGuid;
25749  return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
25750 }
25751 
25752 static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
25753 {
25754  MA_WAVECAPSA caps;
25755 
25756  MA_ASSERT(pContext != NULL);
25757  MA_ASSERT(pCaps != NULL);
25758  MA_ASSERT(pDeviceInfo != NULL);
25759 
25760  MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
25761  caps.dwFormats = pCaps->dwFormats;
25762  caps.wChannels = pCaps->wChannels;
25763  caps.NameGuid = pCaps->NameGuid;
25764  return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
25765 }
25766 
25767 
25768 static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
25769 {
25770  UINT playbackDeviceCount;
25771  UINT captureDeviceCount;
25772  UINT iPlaybackDevice;
25773  UINT iCaptureDevice;
25774 
25775  MA_ASSERT(pContext != NULL);
25776  MA_ASSERT(callback != NULL);
25777 
25778  /* Playback. */
25779  playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
25780  for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
25781  MA_MMRESULT result;
25782  MA_WAVEOUTCAPS2A caps;
25783 
25784  MA_ZERO_OBJECT(&caps);
25785 
25786  result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));
25787  if (result == MA_MMSYSERR_NOERROR) {
25788  ma_device_info deviceInfo;
25789 
25790  MA_ZERO_OBJECT(&deviceInfo);
25791  deviceInfo.id.winmm = iPlaybackDevice;
25792 
25793  /* The first enumerated device is the default device. */
25794  if (iPlaybackDevice == 0) {
25795  deviceInfo.isDefault = MA_TRUE;
25796  }
25797 
25798  if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
25799  ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
25800  if (cbResult == MA_FALSE) {
25801  return MA_SUCCESS; /* Enumeration was stopped. */
25802  }
25803  }
25804  }
25805  }
25806 
25807  /* Capture. */
25808  captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
25809  for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
25810  MA_MMRESULT result;
25811  MA_WAVEINCAPS2A caps;
25812 
25813  MA_ZERO_OBJECT(&caps);
25814 
25815  result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps));
25816  if (result == MA_MMSYSERR_NOERROR) {
25817  ma_device_info deviceInfo;
25818 
25819  MA_ZERO_OBJECT(&deviceInfo);
25820  deviceInfo.id.winmm = iCaptureDevice;
25821 
25822  /* The first enumerated device is the default device. */
25823  if (iCaptureDevice == 0) {
25824  deviceInfo.isDefault = MA_TRUE;
25825  }
25826 
25827  if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
25828  ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
25829  if (cbResult == MA_FALSE) {
25830  return MA_SUCCESS; /* Enumeration was stopped. */
25831  }
25832  }
25833  }
25834  }
25835 
25836  return MA_SUCCESS;
25837 }
25838 
25839 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)
25840 {
25841  UINT winMMDeviceID;
25842 
25843  MA_ASSERT(pContext != NULL);
25844 
25845  winMMDeviceID = 0;
25846  if (pDeviceID != NULL) {
25847  winMMDeviceID = (UINT)pDeviceID->winmm;
25848  }
25849 
25850  pDeviceInfo->id.winmm = winMMDeviceID;
25851 
25852  /* The first ID is the default device. */
25853  if (winMMDeviceID == 0) {
25854  pDeviceInfo->isDefault = MA_TRUE;
25855  }
25856 
25857  if (deviceType == ma_device_type_playback) {
25858  MA_MMRESULT result;
25859  MA_WAVEOUTCAPS2A caps;
25860 
25861  MA_ZERO_OBJECT(&caps);
25862 
25863  result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));
25864  if (result == MA_MMSYSERR_NOERROR) {
25865  return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
25866  }
25867  } else {
25868  MA_MMRESULT result;
25869  MA_WAVEINCAPS2A caps;
25870 
25871  MA_ZERO_OBJECT(&caps);
25872 
25873  result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps));
25874  if (result == MA_MMSYSERR_NOERROR) {
25875  return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
25876  }
25877  }
25878 
25879  return MA_NO_DEVICE;
25880 }
25881 
25882 
25883 static ma_result ma_device_uninit__winmm(ma_device* pDevice)
25884 {
25885  MA_ASSERT(pDevice != NULL);
25886 
25887  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25888  ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
25889  CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
25890  }
25891 
25892  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25893  ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
25894  ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
25895  CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
25896  }
25897 
25898  ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
25899 
25900  MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
25901 
25902  return MA_SUCCESS;
25903 }
25904 
25905 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
25906 {
25907  /* WinMM has a minimum period size of 40ms. */
25908  ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate);
25909  ma_uint32 periodSizeInFrames;
25910 
25911  periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
25912  if (periodSizeInFrames < minPeriodSizeInFrames) {
25913  periodSizeInFrames = minPeriodSizeInFrames;
25914  }
25915 
25916  return periodSizeInFrames;
25917 }
25918 
25919 static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
25920 {
25921  const char* errorMsg = "";
25922  ma_result errorCode = MA_ERROR;
25923  ma_result result = MA_SUCCESS;
25924  ma_uint32 heapSize;
25925  UINT winMMDeviceIDPlayback = 0;
25926  UINT winMMDeviceIDCapture = 0;
25927 
25928  MA_ASSERT(pDevice != NULL);
25929 
25930  MA_ZERO_OBJECT(&pDevice->winmm);
25931 
25932  if (pConfig->deviceType == ma_device_type_loopback) {
25933  return MA_DEVICE_TYPE_NOT_SUPPORTED;
25934  }
25935 
25936  /* No exlusive mode with WinMM. */
25937  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
25938  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
25939  return MA_SHARE_MODE_NOT_SUPPORTED;
25940  }
25941 
25942  if (pDescriptorPlayback->pDeviceID != NULL) {
25943  winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm;
25944  }
25945  if (pDescriptorCapture->pDeviceID != NULL) {
25946  winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm;
25947  }
25948 
25949  /* The capture device needs to be initialized first. */
25950  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
25951  MA_WAVEINCAPSA caps;
25952  MA_WAVEFORMATEX wf;
25953  MA_MMRESULT resultMM;
25954 
25955  /* We use an event to know when a new fragment needs to be enqueued. */
25956  pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
25957  if (pDevice->winmm.hEventCapture == NULL) {
25958  errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
25959  goto on_error;
25960  }
25961 
25962  /* The format should be based on the device's actual format. */
25963  if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {
25964  errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
25965  goto on_error;
25966  }
25967 
25968  result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
25969  if (result != MA_SUCCESS) {
25970  errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
25971  goto on_error;
25972  }
25973 
25974  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);
25975  if (resultMM != MA_MMSYSERR_NOERROR) {
25976  errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
25977  goto on_error;
25978  }
25979 
25980  pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf);
25981  pDescriptorCapture->channels = wf.nChannels;
25982  pDescriptorCapture->sampleRate = wf.nSamplesPerSec;
25983  ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
25984  pDescriptorCapture->periodCount = pDescriptorCapture->periodCount;
25985  pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
25986  }
25987 
25988  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
25989  MA_WAVEOUTCAPSA caps;
25990  MA_WAVEFORMATEX wf;
25991  MA_MMRESULT resultMM;
25992 
25993  /* We use an event to know when a new fragment needs to be enqueued. */
25994  pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
25995  if (pDevice->winmm.hEventPlayback == NULL) {
25996  errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
25997  goto on_error;
25998  }
25999 
26000  /* The format should be based on the device's actual format. */
26001  if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {
26002  errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
26003  goto on_error;
26004  }
26005 
26006  result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
26007  if (result != MA_SUCCESS) {
26008  errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
26009  goto on_error;
26010  }
26011 
26012  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);
26013  if (resultMM != MA_MMSYSERR_NOERROR) {
26014  errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
26015  goto on_error;
26016  }
26017 
26018  pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf);
26019  pDescriptorPlayback->channels = wf.nChannels;
26020  pDescriptorPlayback->sampleRate = wf.nSamplesPerSec;
26021  ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
26022  pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount;
26023  pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
26024  }
26025 
26026  /*
26027  The heap allocated data is allocated like so:
26028 
26029  [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
26030  */
26031  heapSize = 0;
26032  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26033  heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
26034  }
26035  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26036  heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels));
26037  }
26038 
26039  pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks);
26040  if (pDevice->winmm._pHeapData == NULL) {
26041  errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
26042  goto on_error;
26043  }
26044 
26045  MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
26046 
26047  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26048  ma_uint32 iPeriod;
26049 
26050  if (pConfig->deviceType == ma_device_type_capture) {
26051  pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
26052  pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));
26053  } else {
26054  pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
26055  pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount));
26056  }
26057 
26058  /* Prepare headers. */
26059  for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
26060  ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels);
26061 
26062  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
26063  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
26064  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
26065  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
26066  ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
26067 
26068  /*
26069  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
26070  it's unlocked and available for writing. A value of 1 means it's locked.
26071  */
26072  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
26073  }
26074  }
26075 
26076  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26077  ma_uint32 iPeriod;
26078 
26079  if (pConfig->deviceType == ma_device_type_playback) {
26080  pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
26081  pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount);
26082  } else {
26083  pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));
26084  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));
26085  }
26086 
26087  /* Prepare headers. */
26088  for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
26089  ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels);
26090 
26091  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
26092  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
26093  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
26094  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
26095  ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));
26096 
26097  /*
26098  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
26099  it's unlocked and available for writing. A value of 1 means it's locked.
26100  */
26101  ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
26102  }
26103  }
26104 
26105  return MA_SUCCESS;
26106 
26107 on_error:
26108  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26109  if (pDevice->winmm.pWAVEHDRCapture != NULL) {
26110  ma_uint32 iPeriod;
26111  for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
26112  ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
26113  }
26114  }
26115 
26116  ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
26117  }
26118 
26119  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26120  if (pDevice->winmm.pWAVEHDRCapture != NULL) {
26121  ma_uint32 iPeriod;
26122  for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
26123  ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));
26124  }
26125  }
26126 
26127  ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
26128  }
26129 
26130  ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
26131 
26132  if (errorMsg != NULL && errorMsg[0] != '\0') {
26133  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg);
26134  }
26135 
26136  return errorCode;
26137 }
26138 
26139 static ma_result ma_device_start__winmm(ma_device* pDevice)
26140 {
26141  MA_ASSERT(pDevice != NULL);
26142 
26143  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26144  MA_MMRESULT resultMM;
26145  MA_WAVEHDR* pWAVEHDR;
26146  ma_uint32 iPeriod;
26147 
26148  pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
26149 
26150  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
26151  ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
26152 
26153  /* 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. */
26154  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
26155  resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
26156  if (resultMM != MA_MMSYSERR_NOERROR) {
26157  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.");
26158  return ma_result_from_MMRESULT(resultMM);
26159  }
26160 
26161  /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
26162  pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
26163  }
26164 
26165  /* Capture devices need to be explicitly started, unlike playback devices. */
26166  resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
26167  if (resultMM != MA_MMSYSERR_NOERROR) {
26168  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.");
26169  return ma_result_from_MMRESULT(resultMM);
26170  }
26171  }
26172 
26173  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26174  /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */
26175  }
26176 
26177  return MA_SUCCESS;
26178 }
26179 
26180 static ma_result ma_device_stop__winmm(ma_device* pDevice)
26181 {
26182  MA_MMRESULT resultMM;
26183 
26184  MA_ASSERT(pDevice != NULL);
26185 
26186  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26187  if (pDevice->winmm.hDeviceCapture == NULL) {
26188  return MA_INVALID_ARGS;
26189  }
26190 
26191  resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
26192  if (resultMM != MA_MMSYSERR_NOERROR) {
26193  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device.");
26194  }
26195  }
26196 
26197  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26198  ma_uint32 iPeriod;
26199  MA_WAVEHDR* pWAVEHDR;
26200 
26201  if (pDevice->winmm.hDevicePlayback == NULL) {
26202  return MA_INVALID_ARGS;
26203  }
26204 
26205  /* 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. */
26206  pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
26207  for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
26208  if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
26209  if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
26210  break; /* An error occurred so just abandon ship and stop the device without draining. */
26211  }
26212 
26213  pWAVEHDR[iPeriod].dwUser = 0;
26214  }
26215  }
26216 
26217  resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
26218  if (resultMM != MA_MMSYSERR_NOERROR) {
26219  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device.");
26220  }
26221  }
26222 
26223  return MA_SUCCESS;
26224 }
26225 
26226 static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
26227 {
26228  ma_result result = MA_SUCCESS;
26229  MA_MMRESULT resultMM;
26230  ma_uint32 totalFramesWritten;
26231  MA_WAVEHDR* pWAVEHDR;
26232 
26233  MA_ASSERT(pDevice != NULL);
26234  MA_ASSERT(pPCMFrames != NULL);
26235 
26236  if (pFramesWritten != NULL) {
26237  *pFramesWritten = 0;
26238  }
26239 
26240  pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
26241 
26242  /* Keep processing as much data as possible. */
26243  totalFramesWritten = 0;
26244  while (totalFramesWritten < frameCount) {
26245  /* If the current header has some space available we need to write part of it. */
26246  if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
26247  /*
26248  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
26249  write it out and move on to the next iteration.
26250  */
26251  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26252  ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
26253 
26254  ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
26255  const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
26256  void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
26257  MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
26258 
26259  pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
26260  totalFramesWritten += framesToCopy;
26261 
26262  /* If we've consumed the buffer entirely we need to write it out to the device. */
26263  if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
26264  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
26265  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
26266 
26267  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
26268  ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
26269 
26270  /* The device will be started here. */
26271  resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR));
26272  if (resultMM != MA_MMSYSERR_NOERROR) {
26273  result = ma_result_from_MMRESULT(resultMM);
26274  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.");
26275  break;
26276  }
26277 
26278  /* Make sure we move to the next header. */
26279  pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
26280  pDevice->winmm.headerFramesConsumedPlayback = 0;
26281  }
26282 
26283  /* If at this point we have consumed the entire input buffer we can return. */
26284  MA_ASSERT(totalFramesWritten <= frameCount);
26285  if (totalFramesWritten == frameCount) {
26286  break;
26287  }
26288 
26289  /* Getting here means there's more to process. */
26290  continue;
26291  }
26292 
26293  /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
26294  if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
26295  result = MA_ERROR;
26296  break;
26297  }
26298 
26299  /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
26300  if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) {
26301  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
26302  pDevice->winmm.headerFramesConsumedPlayback = 0;
26303  }
26304 
26305  /* If the device has been stopped we need to break. */
26306  if (ma_device_get_state(pDevice) != ma_device_state_started) {
26307  break;
26308  }
26309  }
26310 
26311  if (pFramesWritten != NULL) {
26312  *pFramesWritten = totalFramesWritten;
26313  }
26314 
26315  return result;
26316 }
26317 
26318 static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
26319 {
26320  ma_result result = MA_SUCCESS;
26321  MA_MMRESULT resultMM;
26322  ma_uint32 totalFramesRead;
26323  MA_WAVEHDR* pWAVEHDR;
26324 
26325  MA_ASSERT(pDevice != NULL);
26326  MA_ASSERT(pPCMFrames != NULL);
26327 
26328  if (pFramesRead != NULL) {
26329  *pFramesRead = 0;
26330  }
26331 
26332  pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
26333 
26334  /* Keep processing as much data as possible. */
26335  totalFramesRead = 0;
26336  while (totalFramesRead < frameCount) {
26337  /* If the current header has some space available we need to write part of it. */
26338  if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
26339  /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
26340  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26341  ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
26342 
26343  ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
26344  const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
26345  void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
26346  MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
26347 
26348  pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
26349  totalFramesRead += framesToCopy;
26350 
26351  /* If we've consumed the buffer entirely we need to add it back to the device. */
26352  if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
26353  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
26354  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
26355 
26356  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
26357  ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
26358 
26359  /* The device will be started here. */
26360  resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR));
26361  if (resultMM != MA_MMSYSERR_NOERROR) {
26362  result = ma_result_from_MMRESULT(resultMM);
26363  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.");
26364  break;
26365  }
26366 
26367  /* Make sure we move to the next header. */
26368  pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
26369  pDevice->winmm.headerFramesConsumedCapture = 0;
26370  }
26371 
26372  /* If at this point we have filled the entire input buffer we can return. */
26373  MA_ASSERT(totalFramesRead <= frameCount);
26374  if (totalFramesRead == frameCount) {
26375  break;
26376  }
26377 
26378  /* Getting here means there's more to process. */
26379  continue;
26380  }
26381 
26382  /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
26383  if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
26384  result = MA_ERROR;
26385  break;
26386  }
26387 
26388  /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
26389  if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) {
26390  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
26391  pDevice->winmm.headerFramesConsumedCapture = 0;
26392  }
26393 
26394  /* If the device has been stopped we need to break. */
26395  if (ma_device_get_state(pDevice) != ma_device_state_started) {
26396  break;
26397  }
26398  }
26399 
26400  if (pFramesRead != NULL) {
26401  *pFramesRead = totalFramesRead;
26402  }
26403 
26404  return result;
26405 }
26406 
26407 static ma_result ma_context_uninit__winmm(ma_context* pContext)
26408 {
26409  MA_ASSERT(pContext != NULL);
26410  MA_ASSERT(pContext->backend == ma_backend_winmm);
26411 
26412  ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM);
26413  return MA_SUCCESS;
26414 }
26415 
26416 static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
26417 {
26418  MA_ASSERT(pContext != NULL);
26419 
26420  (void)pConfig;
26421 
26422  pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll");
26423  if (pContext->winmm.hWinMM == NULL) {
26424  return MA_NO_BACKEND;
26425  }
26426 
26427  pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs");
26428  pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA");
26429  pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen");
26430  pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose");
26431  pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader");
26432  pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader");
26433  pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite");
26434  pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset");
26435  pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs");
26436  pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA");
26437  pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen");
26438  pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose");
26439  pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader");
26440  pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader");
26441  pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer");
26442  pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart");
26443  pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset");
26444 
26445  pCallbacks->onContextInit = ma_context_init__winmm;
26446  pCallbacks->onContextUninit = ma_context_uninit__winmm;
26447  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;
26448  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm;
26449  pCallbacks->onDeviceInit = ma_device_init__winmm;
26450  pCallbacks->onDeviceUninit = ma_device_uninit__winmm;
26451  pCallbacks->onDeviceStart = ma_device_start__winmm;
26452  pCallbacks->onDeviceStop = ma_device_stop__winmm;
26453  pCallbacks->onDeviceRead = ma_device_read__winmm;
26454  pCallbacks->onDeviceWrite = ma_device_write__winmm;
26455  pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */
26456 
26457  return MA_SUCCESS;
26458 }
26459 #endif
26460 
26461 
26462 
26463 
26464 /******************************************************************************
26465 
26466 ALSA Backend
26467 
26468 ******************************************************************************/
26469 #ifdef MA_HAS_ALSA
26470 
26471 #include <poll.h> /* poll(), struct pollfd */
26472 #include <sys/eventfd.h> /* eventfd() */
26473 
26474 #ifdef MA_NO_RUNTIME_LINKING
26475 
26476 /* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
26477 #if !defined(__cplusplus)
26478  #if defined(__STRICT_ANSI__)
26479  #if !defined(inline)
26480  #define inline __inline__ __attribute__((always_inline))
26481  #define MA_INLINE_DEFINED
26482  #endif
26483  #endif
26484 #endif
26485 #include <alsa/asoundlib.h>
26486 #if defined(MA_INLINE_DEFINED)
26487  #undef inline
26488  #undef MA_INLINE_DEFINED
26489 #endif
26490 
26491 typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
26492 typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
26493 typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
26494 typedef snd_pcm_format_t ma_snd_pcm_format_t;
26495 typedef snd_pcm_access_t ma_snd_pcm_access_t;
26496 typedef snd_pcm_t ma_snd_pcm_t;
26497 typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
26498 typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
26499 typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
26500 typedef snd_pcm_info_t ma_snd_pcm_info_t;
26501 typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
26502 typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
26503 typedef snd_pcm_state_t ma_snd_pcm_state_t;
26504 
26505 /* snd_pcm_stream_t */
26506 #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
26507 #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
26508 
26509 /* snd_pcm_format_t */
26510 #define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
26511 #define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
26512 #define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
26513 #define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
26514 #define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
26515 #define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
26516 #define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
26517 #define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
26518 #define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
26519 #define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
26520 #define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
26521 #define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
26522 #define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
26523 #define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
26524 #define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
26525 #define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
26526 
26527 /* ma_snd_pcm_access_t */
26528 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
26529 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
26530 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
26531 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
26532 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
26533 
26534 /* Channel positions. */
26535 #define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
26536 #define MA_SND_CHMAP_NA SND_CHMAP_NA
26537 #define MA_SND_CHMAP_MONO SND_CHMAP_MONO
26538 #define MA_SND_CHMAP_FL SND_CHMAP_FL
26539 #define MA_SND_CHMAP_FR SND_CHMAP_FR
26540 #define MA_SND_CHMAP_RL SND_CHMAP_RL
26541 #define MA_SND_CHMAP_RR SND_CHMAP_RR
26542 #define MA_SND_CHMAP_FC SND_CHMAP_FC
26543 #define MA_SND_CHMAP_LFE SND_CHMAP_LFE
26544 #define MA_SND_CHMAP_SL SND_CHMAP_SL
26545 #define MA_SND_CHMAP_SR SND_CHMAP_SR
26546 #define MA_SND_CHMAP_RC SND_CHMAP_RC
26547 #define MA_SND_CHMAP_FLC SND_CHMAP_FLC
26548 #define MA_SND_CHMAP_FRC SND_CHMAP_FRC
26549 #define MA_SND_CHMAP_RLC SND_CHMAP_RLC
26550 #define MA_SND_CHMAP_RRC SND_CHMAP_RRC
26551 #define MA_SND_CHMAP_FLW SND_CHMAP_FLW
26552 #define MA_SND_CHMAP_FRW SND_CHMAP_FRW
26553 #define MA_SND_CHMAP_FLH SND_CHMAP_FLH
26554 #define MA_SND_CHMAP_FCH SND_CHMAP_FCH
26555 #define MA_SND_CHMAP_FRH SND_CHMAP_FRH
26556 #define MA_SND_CHMAP_TC SND_CHMAP_TC
26557 #define MA_SND_CHMAP_TFL SND_CHMAP_TFL
26558 #define MA_SND_CHMAP_TFR SND_CHMAP_TFR
26559 #define MA_SND_CHMAP_TFC SND_CHMAP_TFC
26560 #define MA_SND_CHMAP_TRL SND_CHMAP_TRL
26561 #define MA_SND_CHMAP_TRR SND_CHMAP_TRR
26562 #define MA_SND_CHMAP_TRC SND_CHMAP_TRC
26563 #define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
26564 #define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
26565 #define MA_SND_CHMAP_TSL SND_CHMAP_TSL
26566 #define MA_SND_CHMAP_TSR SND_CHMAP_TSR
26567 #define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
26568 #define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
26569 #define MA_SND_CHMAP_BC SND_CHMAP_BC
26570 #define MA_SND_CHMAP_BLC SND_CHMAP_BLC
26571 #define MA_SND_CHMAP_BRC SND_CHMAP_BRC
26572 
26573 /* Open mode flags. */
26574 #define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
26575 #define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
26576 #define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
26577 #else
26578 #include <errno.h> /* For EPIPE, etc. */
26579 typedef unsigned long ma_snd_pcm_uframes_t;
26580 typedef long ma_snd_pcm_sframes_t;
26581 typedef int ma_snd_pcm_stream_t;
26582 typedef int ma_snd_pcm_format_t;
26583 typedef int ma_snd_pcm_access_t;
26584 typedef int ma_snd_pcm_state_t;
26585 typedef struct ma_snd_pcm_t ma_snd_pcm_t;
26586 typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
26587 typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
26588 typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
26589 typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
26590 typedef struct
26591 {
26592  void* addr;
26593  unsigned int first;
26594  unsigned int step;
26595 } ma_snd_pcm_channel_area_t;
26596 typedef struct
26597 {
26598  unsigned int channels;
26599  unsigned int pos[1];
26600 } ma_snd_pcm_chmap_t;
26601 
26602 /* snd_pcm_state_t */
26603 #define MA_SND_PCM_STATE_OPEN 0
26604 #define MA_SND_PCM_STATE_SETUP 1
26605 #define MA_SND_PCM_STATE_PREPARED 2
26606 #define MA_SND_PCM_STATE_RUNNING 3
26607 #define MA_SND_PCM_STATE_XRUN 4
26608 #define MA_SND_PCM_STATE_DRAINING 5
26609 #define MA_SND_PCM_STATE_PAUSED 6
26610 #define MA_SND_PCM_STATE_SUSPENDED 7
26611 #define MA_SND_PCM_STATE_DISCONNECTED 8
26612 
26613 /* snd_pcm_stream_t */
26614 #define MA_SND_PCM_STREAM_PLAYBACK 0
26615 #define MA_SND_PCM_STREAM_CAPTURE 1
26616 
26617 /* snd_pcm_format_t */
26618 #define MA_SND_PCM_FORMAT_UNKNOWN -1
26619 #define MA_SND_PCM_FORMAT_U8 1
26620 #define MA_SND_PCM_FORMAT_S16_LE 2
26621 #define MA_SND_PCM_FORMAT_S16_BE 3
26622 #define MA_SND_PCM_FORMAT_S24_LE 6
26623 #define MA_SND_PCM_FORMAT_S24_BE 7
26624 #define MA_SND_PCM_FORMAT_S32_LE 10
26625 #define MA_SND_PCM_FORMAT_S32_BE 11
26626 #define MA_SND_PCM_FORMAT_FLOAT_LE 14
26627 #define MA_SND_PCM_FORMAT_FLOAT_BE 15
26628 #define MA_SND_PCM_FORMAT_FLOAT64_LE 16
26629 #define MA_SND_PCM_FORMAT_FLOAT64_BE 17
26630 #define MA_SND_PCM_FORMAT_MU_LAW 20
26631 #define MA_SND_PCM_FORMAT_A_LAW 21
26632 #define MA_SND_PCM_FORMAT_S24_3LE 32
26633 #define MA_SND_PCM_FORMAT_S24_3BE 33
26634 
26635 /* snd_pcm_access_t */
26636 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
26637 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
26638 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
26639 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
26640 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
26641 
26642 /* Channel positions. */
26643 #define MA_SND_CHMAP_UNKNOWN 0
26644 #define MA_SND_CHMAP_NA 1
26645 #define MA_SND_CHMAP_MONO 2
26646 #define MA_SND_CHMAP_FL 3
26647 #define MA_SND_CHMAP_FR 4
26648 #define MA_SND_CHMAP_RL 5
26649 #define MA_SND_CHMAP_RR 6
26650 #define MA_SND_CHMAP_FC 7
26651 #define MA_SND_CHMAP_LFE 8
26652 #define MA_SND_CHMAP_SL 9
26653 #define MA_SND_CHMAP_SR 10
26654 #define MA_SND_CHMAP_RC 11
26655 #define MA_SND_CHMAP_FLC 12
26656 #define MA_SND_CHMAP_FRC 13
26657 #define MA_SND_CHMAP_RLC 14
26658 #define MA_SND_CHMAP_RRC 15
26659 #define MA_SND_CHMAP_FLW 16
26660 #define MA_SND_CHMAP_FRW 17
26661 #define MA_SND_CHMAP_FLH 18
26662 #define MA_SND_CHMAP_FCH 19
26663 #define MA_SND_CHMAP_FRH 20
26664 #define MA_SND_CHMAP_TC 21
26665 #define MA_SND_CHMAP_TFL 22
26666 #define MA_SND_CHMAP_TFR 23
26667 #define MA_SND_CHMAP_TFC 24
26668 #define MA_SND_CHMAP_TRL 25
26669 #define MA_SND_CHMAP_TRR 26
26670 #define MA_SND_CHMAP_TRC 27
26671 #define MA_SND_CHMAP_TFLC 28
26672 #define MA_SND_CHMAP_TFRC 29
26673 #define MA_SND_CHMAP_TSL 30
26674 #define MA_SND_CHMAP_TSR 31
26675 #define MA_SND_CHMAP_LLFE 32
26676 #define MA_SND_CHMAP_RLFE 33
26677 #define MA_SND_CHMAP_BC 34
26678 #define MA_SND_CHMAP_BLC 35
26679 #define MA_SND_CHMAP_BRC 36
26680 
26681 /* Open mode flags. */
26682 #define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
26683 #define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
26684 #define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
26685 #endif
26686 
26687 typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
26688 typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
26689 typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
26690 typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
26691 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);
26692 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);
26693 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);
26694 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);
26695 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);
26696 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);
26697 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);
26698 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);
26699 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);
26700 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);
26701 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);
26702 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);
26703 typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
26704 typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
26705 typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
26706 typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
26707 typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
26708 typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
26709 typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
26710 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);
26711 typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
26712 typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
26713 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);
26714 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);
26715 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);
26716 typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
26717 typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
26718 typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
26719 typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
26720 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);
26721 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);
26722 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);
26723 typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
26724 typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
26725 typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
26726 typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
26727 typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
26728 typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
26729 typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
26730 typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
26731 typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
26732 typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm);
26733 typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
26734 typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
26735 typedef int (* ma_snd_card_get_index_proc) (const char *name);
26736 typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
26737 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);
26738 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);
26739 typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
26740 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
26741 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);
26742 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
26743 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
26744 typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
26745 typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock);
26746 typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
26747 typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void);
26748 typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
26749 typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
26750 typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm);
26751 typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
26752 typedef int (* ma_snd_config_update_free_global_proc) (void);
26753 
26754 /* This array specifies each of the common devices that can be used for both playback and capture. */
26755 static const char* g_maCommonDeviceNamesALSA[] = {
26756  "default",
26757  "null",
26758  "pulse",
26759  "jack"
26760 };
26761 
26762 /* This array allows us to blacklist specific playback devices. */
26763 static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
26764  ""
26765 };
26766 
26767 /* This array allows us to blacklist specific capture devices. */
26768 static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
26769  ""
26770 };
26771 
26772 
26773 static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
26774 {
26775  ma_snd_pcm_format_t ALSAFormats[] = {
26776  MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
26777  MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
26778  MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
26779  MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
26780  MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
26781  MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
26782  };
26783 
26784  if (ma_is_big_endian()) {
26785  ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
26786  ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
26787  ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
26788  ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
26789  ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
26790  ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
26791  }
26792 
26793  return ALSAFormats[format];
26794 }
26795 
26796 static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
26797 {
26798  if (ma_is_little_endian()) {
26799  switch (formatALSA) {
26800  case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
26801  case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
26802  case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
26803  case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
26804  default: break;
26805  }
26806  } else {
26807  switch (formatALSA) {
26808  case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
26809  case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
26810  case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
26811  case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
26812  default: break;
26813  }
26814  }
26815 
26816  /* Endian agnostic. */
26817  switch (formatALSA) {
26818  case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
26819  default: return ma_format_unknown;
26820  }
26821 }
26822 
26823 static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
26824 {
26825  switch (alsaChannelPos)
26826  {
26827  case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
26828  case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
26829  case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
26830  case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
26831  case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
26832  case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
26833  case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
26834  case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
26835  case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
26836  case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
26837  case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
26838  case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
26839  case MA_SND_CHMAP_RLC: return 0;
26840  case MA_SND_CHMAP_RRC: return 0;
26841  case MA_SND_CHMAP_FLW: return 0;
26842  case MA_SND_CHMAP_FRW: return 0;
26843  case MA_SND_CHMAP_FLH: return 0;
26844  case MA_SND_CHMAP_FCH: return 0;
26845  case MA_SND_CHMAP_FRH: return 0;
26846  case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
26847  case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
26848  case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
26849  case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
26850  case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
26851  case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
26852  case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
26853  default: break;
26854  }
26855 
26856  return 0;
26857 }
26858 
26859 static ma_bool32 ma_is_common_device_name__alsa(const char* name)
26860 {
26861  size_t iName;
26862  for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
26863  if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
26864  return MA_TRUE;
26865  }
26866  }
26867 
26868  return MA_FALSE;
26869 }
26870 
26871 
26872 static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
26873 {
26874  size_t iName;
26875  for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
26876  if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
26877  return MA_TRUE;
26878  }
26879  }
26880 
26881  return MA_FALSE;
26882 }
26883 
26884 static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
26885 {
26886  size_t iName;
26887  for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
26888  if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
26889  return MA_TRUE;
26890  }
26891  }
26892 
26893  return MA_FALSE;
26894 }
26895 
26896 static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
26897 {
26898  if (deviceType == ma_device_type_playback) {
26899  return ma_is_playback_device_blacklisted__alsa(name);
26900  } else {
26901  return ma_is_capture_device_blacklisted__alsa(name);
26902  }
26903 }
26904 
26905 
26906 static const char* ma_find_char(const char* str, char c, int* index)
26907 {
26908  int i = 0;
26909  for (;;) {
26910  if (str[i] == '\0') {
26911  if (index) *index = -1;
26912  return NULL;
26913  }
26914 
26915  if (str[i] == c) {
26916  if (index) *index = i;
26917  return str + i;
26918  }
26919 
26920  i += 1;
26921  }
26922 
26923  /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
26924  if (index) *index = -1;
26925  return NULL;
26926 }
26927 
26928 static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
26929 {
26930  /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
26931 
26932  int commaPos;
26933  const char* dev;
26934  int i;
26935 
26936  if (hwid == NULL) {
26937  return MA_FALSE;
26938  }
26939 
26940  if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
26941  return MA_FALSE;
26942  }
26943 
26944  hwid += 3;
26945 
26946  dev = ma_find_char(hwid, ',', &commaPos);
26947  if (dev == NULL) {
26948  return MA_FALSE;
26949  } else {
26950  dev += 1; /* Skip past the ",". */
26951  }
26952 
26953  /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
26954  for (i = 0; i < commaPos; ++i) {
26955  if (hwid[i] < '0' || hwid[i] > '9') {
26956  return MA_FALSE;
26957  }
26958  }
26959 
26960  /* Check if everything after the "," is numeric. If not, return false. */
26961  i = 0;
26962  while (dev[i] != '\0') {
26963  if (dev[i] < '0' || dev[i] > '9') {
26964  return MA_FALSE;
26965  }
26966  i += 1;
26967  }
26968 
26969  return MA_TRUE;
26970 }
26971 
26972 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. */
26973 {
26974  /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
26975 
26976  int colonPos;
26977  int commaPos;
26978  char card[256];
26979  const char* dev;
26980  int cardIndex;
26981 
26982  if (dst == NULL) {
26983  return -1;
26984  }
26985  if (dstSize < 7) {
26986  return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
26987  }
26988 
26989  *dst = '\0'; /* Safety. */
26990  if (src == NULL) {
26991  return -1;
26992  }
26993 
26994  /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
26995  if (ma_is_device_name_in_hw_format__alsa(src)) {
26996  return ma_strcpy_s(dst, dstSize, src);
26997  }
26998 
26999  src = ma_find_char(src, ':', &colonPos);
27000  if (src == NULL) {
27001  return -1; /* Couldn't find a colon */
27002  }
27003 
27004  dev = ma_find_char(src, ',', &commaPos);
27005  if (dev == NULL) {
27006  dev = "0";
27007  ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
27008  } else {
27009  dev = dev + 5; /* +5 = ",DEV=" */
27010  ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
27011  }
27012 
27013  cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
27014  if (cardIndex < 0) {
27015  return -2; /* Failed to retrieve the card index. */
27016  }
27017 
27018 
27019  /* Construction. */
27020  dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
27021  if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
27022  return -3;
27023  }
27024  if (ma_strcat_s(dst, dstSize, ",") != 0) {
27025  return -3;
27026  }
27027  if (ma_strcat_s(dst, dstSize, dev) != 0) {
27028  return -3;
27029  }
27030 
27031  return 0;
27032 }
27033 
27034 static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
27035 {
27036  ma_uint32 i;
27037 
27038  MA_ASSERT(pHWID != NULL);
27039 
27040  for (i = 0; i < count; ++i) {
27041  if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
27042  return MA_TRUE;
27043  }
27044  }
27045 
27046  return MA_FALSE;
27047 }
27048 
27049 
27050 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)
27051 {
27052  ma_snd_pcm_t* pPCM;
27053  ma_snd_pcm_stream_t stream;
27054 
27055  MA_ASSERT(pContext != NULL);
27056  MA_ASSERT(ppPCM != NULL);
27057 
27058  *ppPCM = NULL;
27059  pPCM = NULL;
27060 
27061  stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
27062 
27063  if (pDeviceID == NULL) {
27064  ma_bool32 isDeviceOpen;
27065  size_t i;
27066 
27067  /*
27068  We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
27069  me feel better to try as hard as we can get to get _something_ working.
27070  */
27071  const char* defaultDeviceNames[] = {
27072  "default",
27073  NULL,
27074  NULL,
27075  NULL,
27076  NULL,
27077  NULL,
27078  NULL
27079  };
27080 
27081  if (shareMode == ma_share_mode_exclusive) {
27082  defaultDeviceNames[1] = "hw";
27083  defaultDeviceNames[2] = "hw:0";
27084  defaultDeviceNames[3] = "hw:0,0";
27085  } else {
27086  if (deviceType == ma_device_type_playback) {
27087  defaultDeviceNames[1] = "dmix";
27088  defaultDeviceNames[2] = "dmix:0";
27089  defaultDeviceNames[3] = "dmix:0,0";
27090  } else {
27091  defaultDeviceNames[1] = "dsnoop";
27092  defaultDeviceNames[2] = "dsnoop:0";
27093  defaultDeviceNames[3] = "dsnoop:0,0";
27094  }
27095  defaultDeviceNames[4] = "hw";
27096  defaultDeviceNames[5] = "hw:0";
27097  defaultDeviceNames[6] = "hw:0,0";
27098  }
27099 
27100  isDeviceOpen = MA_FALSE;
27101  for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
27102  if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
27103  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
27104  isDeviceOpen = MA_TRUE;
27105  break;
27106  }
27107  }
27108  }
27109 
27110  if (!isDeviceOpen) {
27111  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.");
27112  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
27113  }
27114  } else {
27115  /*
27116  We're trying to open a specific device. There's a few things to consider here:
27117 
27118  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
27119  an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
27120  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").
27121  */
27122 
27123  /* May end up needing to make small adjustments to the ID, so make a copy. */
27124  ma_device_id deviceID = *pDeviceID;
27125  int resultALSA = -ENODEV;
27126 
27127  if (deviceID.alsa[0] != ':') {
27128  /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
27129  resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
27130  } else {
27131  char hwid[256];
27132 
27133  /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
27134  if (deviceID.alsa[1] == '\0') {
27135  deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
27136  }
27137 
27138  if (shareMode == ma_share_mode_shared) {
27139  if (deviceType == ma_device_type_playback) {
27140  ma_strcpy_s(hwid, sizeof(hwid), "dmix");
27141  } else {
27142  ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
27143  }
27144 
27145  if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
27146  resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
27147  }
27148  }
27149 
27150  /* 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. */
27151  if (resultALSA != 0) {
27152  ma_strcpy_s(hwid, sizeof(hwid), "hw");
27153  if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
27154  resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
27155  }
27156  }
27157  }
27158 
27159  if (resultALSA < 0) {
27160  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.");
27161  return ma_result_from_errno(-resultALSA);
27162  }
27163  }
27164 
27165  *ppPCM = pPCM;
27166  return MA_SUCCESS;
27167 }
27168 
27169 
27170 static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
27171 {
27172  int resultALSA;
27173  ma_bool32 cbResult = MA_TRUE;
27174  char** ppDeviceHints;
27175  ma_device_id* pUniqueIDs = NULL;
27176  ma_uint32 uniqueIDCount = 0;
27177  char** ppNextDeviceHint;
27178 
27179  MA_ASSERT(pContext != NULL);
27180  MA_ASSERT(callback != NULL);
27181 
27182  ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
27183 
27184  resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
27185  if (resultALSA < 0) {
27186  ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
27187  return ma_result_from_errno(-resultALSA);
27188  }
27189 
27190  ppNextDeviceHint = ppDeviceHints;
27191  while (*ppNextDeviceHint != NULL) {
27192  char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
27193  char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
27194  char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
27195  ma_device_type deviceType = ma_device_type_playback;
27196  ma_bool32 stopEnumeration = MA_FALSE;
27197  char hwid[sizeof(pUniqueIDs->alsa)];
27198  ma_device_info deviceInfo;
27199 
27200  if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
27201  deviceType = ma_device_type_playback;
27202  }
27203  if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
27204  deviceType = ma_device_type_capture;
27205  }
27206 
27207  if (NAME != NULL) {
27208  if (pContext->alsa.useVerboseDeviceEnumeration) {
27209  /* Verbose mode. Use the name exactly as-is. */
27210  ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
27211  } else {
27212  /* Simplified mode. Use ":%d,%d" format. */
27213  if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
27214  /*
27215  At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
27216  plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
27217  initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
27218  device type and sharing mode.
27219  */
27220  char* dst = hwid;
27221  char* src = hwid+2;
27222  while ((*dst++ = *src++));
27223  } else {
27224  /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
27225  ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
27226  }
27227 
27228  if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
27229  goto next_device; /* The device has already been enumerated. Move on to the next one. */
27230  } else {
27231  /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
27232  size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
27233  ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks);
27234  if (pNewUniqueIDs == NULL) {
27235  goto next_device; /* Failed to allocate memory. */
27236  }
27237 
27238  pUniqueIDs = pNewUniqueIDs;
27239  MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
27240  uniqueIDCount += 1;
27241  }
27242  }
27243  } else {
27244  MA_ZERO_MEMORY(hwid, sizeof(hwid));
27245  }
27246 
27247  MA_ZERO_OBJECT(&deviceInfo);
27248  ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
27249 
27250  /*
27251  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
27252  just use the name of "default" as the indicator.
27253  */
27254  if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) {
27255  deviceInfo.isDefault = MA_TRUE;
27256  }
27257 
27258 
27259  /*
27260  DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
27261  device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
27262  between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
27263  description.
27264 
27265  The value in DESC seems to be split into two lines, with the first line being the name of the device and the
27266  second line being a description of the device. I don't like having the description be across two lines because
27267  it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
27268  being put into parentheses. In simplified mode I'm just stripping the second line entirely.
27269  */
27270  if (DESC != NULL) {
27271  int lfPos;
27272  const char* line2 = ma_find_char(DESC, '\n', &lfPos);
27273  if (line2 != NULL) {
27274  line2 += 1; /* Skip past the new-line character. */
27275 
27276  if (pContext->alsa.useVerboseDeviceEnumeration) {
27277  /* Verbose mode. Put the second line in brackets. */
27278  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
27279  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
27280  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
27281  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
27282  } else {
27283  /* Simplified mode. Strip the second line entirely. */
27284  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
27285  }
27286  } else {
27287  /* There's no second line. Just copy the whole description. */
27288  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
27289  }
27290  }
27291 
27292  if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
27293  cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
27294  }
27295 
27296  /*
27297  Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
27298  again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which
27299  means both Input and Output.
27300  */
27301  if (cbResult) {
27302  if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) {
27303  if (deviceType == ma_device_type_playback) {
27304  if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
27305  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
27306  }
27307  } else {
27308  if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
27309  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
27310  }
27311  }
27312  }
27313  }
27314 
27315  if (cbResult == MA_FALSE) {
27316  stopEnumeration = MA_TRUE;
27317  }
27318 
27319  next_device:
27320  free(NAME);
27321  free(DESC);
27322  free(IOID);
27323  ppNextDeviceHint += 1;
27324 
27325  /* We need to stop enumeration if the callback returned false. */
27326  if (stopEnumeration) {
27327  break;
27328  }
27329  }
27330 
27331  ma_free(pUniqueIDs, &pContext->allocationCallbacks);
27332  ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
27333 
27334  ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
27335 
27336  return MA_SUCCESS;
27337 }
27338 
27339 
27340 typedef struct
27341 {
27342  ma_device_type deviceType;
27343  const ma_device_id* pDeviceID;
27344  ma_share_mode shareMode;
27345  ma_device_info* pDeviceInfo;
27346  ma_bool32 foundDevice;
27347 } ma_context_get_device_info_enum_callback_data__alsa;
27348 
27349 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)
27350 {
27351  ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
27352  MA_ASSERT(pData != NULL);
27353 
27354  (void)pContext;
27355 
27356  if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
27357  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
27358  pData->foundDevice = MA_TRUE;
27359  } else {
27360  if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) {
27361  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
27362  pData->foundDevice = MA_TRUE;
27363  }
27364  }
27365 
27366  /* Keep enumerating until we have found the device. */
27367  return !pData->foundDevice;
27368 }
27369 
27370 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)
27371 {
27372  MA_ASSERT(pPCM != NULL);
27373  MA_ASSERT(pHWParams != NULL);
27374  MA_ASSERT(pDeviceInfo != NULL);
27375 
27376  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) {
27377  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
27378  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
27379  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
27380  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
27381  pDeviceInfo->nativeDataFormatCount += 1;
27382  }
27383 }
27384 
27385 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)
27386 {
27387  ma_uint32 iSampleRate;
27388  unsigned int minSampleRate;
27389  unsigned int maxSampleRate;
27390  int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */
27391 
27392  /* There could be a range. */
27393  ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);
27394  ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);
27395 
27396  /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */
27397  minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
27398  maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
27399 
27400  for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
27401  ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
27402 
27403  if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
27404  ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo);
27405  }
27406  }
27407 
27408  /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */
27409  if (!ma_is_standard_sample_rate(minSampleRate)) {
27410  ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo);
27411  }
27412 
27413  if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) {
27414  ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo);
27415  }
27416 }
27417 
27418 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)
27419 {
27420  ma_context_get_device_info_enum_callback_data__alsa data;
27421  ma_result result;
27422  int resultALSA;
27423  ma_snd_pcm_t* pPCM;
27424  ma_snd_pcm_hw_params_t* pHWParams;
27425  ma_uint32 iFormat;
27426  ma_uint32 iChannel;
27427 
27428  MA_ASSERT(pContext != NULL);
27429 
27430  /* We just enumerate to find basic information about the device. */
27431  data.deviceType = deviceType;
27432  data.pDeviceID = pDeviceID;
27433  data.pDeviceInfo = pDeviceInfo;
27434  data.foundDevice = MA_FALSE;
27435  result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
27436  if (result != MA_SUCCESS) {
27437  return result;
27438  }
27439 
27440  if (!data.foundDevice) {
27441  return MA_NO_DEVICE;
27442  }
27443 
27444  if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
27445  pDeviceInfo->isDefault = MA_TRUE;
27446  }
27447 
27448  /* For detailed info we need to open the device. */
27449  result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM);
27450  if (result != MA_SUCCESS) {
27451  return result;
27452  }
27453 
27454  /* We need to initialize a HW parameters object in order to know what formats are supported. */
27455  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);
27456  if (pHWParams == NULL) {
27457  ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
27458  return MA_OUT_OF_MEMORY;
27459  }
27460 
27461  resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
27462  if (resultALSA < 0) {
27463  ma_free(pHWParams, &pContext->allocationCallbacks);
27464  ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
27465  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
27466  return ma_result_from_errno(-resultALSA);
27467  }
27468 
27469  /*
27470  Some ALSA devices can support many permutations of formats, channels and rates. We only support
27471  a fixed number of permutations which means we need to employ some strategies to ensure the best
27472  combinations are returned. An example is the "pulse" device which can do it's own data conversion
27473  in software and as a result can support any combination of format, channels and rate.
27474 
27475  We want to ensure the the first data formats are the best. We have a list of favored sample
27476  formats and sample rates, so these will be the basis of our iteration.
27477  */
27478 
27479  /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
27480  for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
27481  ma_format format = g_maFormatPriorities[iFormat];
27482 
27483  /*
27484  For each format we need to make sure we reset the configuration space so we don't return
27485  channel counts and rates that aren't compatible with a format.
27486  */
27487  ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
27488 
27489  /* Test the format first. If this fails it means the format is not supported and we can skip it. */
27490  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) {
27491  /* The format is supported. */
27492  unsigned int minChannels;
27493  unsigned int maxChannels;
27494 
27495  /*
27496  The configuration space needs to be restricted to this format so we can get an accurate
27497  picture of which sample rates and channel counts are support with this format.
27498  */
27499  ((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));
27500 
27501  /* Now we need to check for supported channels. */
27502  ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels);
27503  ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels);
27504 
27505  if (minChannels > MA_MAX_CHANNELS) {
27506  continue; /* Too many channels. */
27507  }
27508  if (maxChannels < MA_MIN_CHANNELS) {
27509  continue; /* Not enough channels. */
27510  }
27511 
27512  /*
27513  Make sure the channel count is clamped. This is mainly intended for the max channels
27514  because some devices can report an unbound maximum.
27515  */
27516  minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
27517  maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
27518 
27519  if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
27520  /* 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. */
27521  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. */
27522  } else {
27523  /* The device only supports a specific set of channels. We need to iterate over all of them. */
27524  for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
27525  /* Test the channel before applying it to the configuration space. */
27526  unsigned int channels = iChannel;
27527 
27528  /* Make sure our channel range is reset before testing again or else we'll always fail the test. */
27529  ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
27530  ((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));
27531 
27532  if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) {
27533  /* The channel count is supported. */
27534 
27535  /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */
27536  ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels);
27537 
27538  /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */
27539  ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo);
27540  } else {
27541  /* The channel count is not supported. Skip. */
27542  }
27543  }
27544  }
27545  } else {
27546  /* The format is not supported. Skip. */
27547  }
27548  }
27549 
27550  ma_free(pHWParams, &pContext->allocationCallbacks);
27551 
27552  ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
27553  return MA_SUCCESS;
27554 }
27555 
27556 static ma_result ma_device_uninit__alsa(ma_device* pDevice)
27557 {
27558  MA_ASSERT(pDevice != NULL);
27559 
27560  if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
27561  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
27562  close(pDevice->alsa.wakeupfdCapture);
27563  ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);
27564  }
27565 
27566  if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
27567  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
27568  close(pDevice->alsa.wakeupfdPlayback);
27569  ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);
27570  }
27571 
27572  return MA_SUCCESS;
27573 }
27574 
27575 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)
27576 {
27577  ma_result result;
27578  int resultALSA;
27579  ma_snd_pcm_t* pPCM;
27580  ma_bool32 isUsingMMap;
27581  ma_snd_pcm_format_t formatALSA;
27582  ma_format internalFormat;
27583  ma_uint32 internalChannels;
27584  ma_uint32 internalSampleRate;
27585  ma_channel internalChannelMap[MA_MAX_CHANNELS];
27586  ma_uint32 internalPeriodSizeInFrames;
27587  ma_uint32 internalPeriods;
27588  int openMode;
27589  ma_snd_pcm_hw_params_t* pHWParams;
27590  ma_snd_pcm_sw_params_t* pSWParams;
27591  ma_snd_pcm_uframes_t bufferBoundary;
27592  int pollDescriptorCount;
27593  struct pollfd* pPollDescriptors;
27594  int wakeupfd;
27595 
27596  MA_ASSERT(pConfig != NULL);
27597  MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
27598  MA_ASSERT(pDevice != NULL);
27599 
27600  formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format);
27601 
27602  openMode = 0;
27603  if (pConfig->alsa.noAutoResample) {
27604  openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;
27605  }
27606  if (pConfig->alsa.noAutoChannels) {
27607  openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;
27608  }
27609  if (pConfig->alsa.noAutoFormat) {
27610  openMode |= MA_SND_PCM_NO_AUTO_FORMAT;
27611  }
27612 
27613  result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM);
27614  if (result != MA_SUCCESS) {
27615  return result;
27616  }
27617 
27618 
27619  /* Hardware parameters. */
27620  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);
27621  if (pHWParams == NULL) {
27622  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27623  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters.");
27624  return MA_OUT_OF_MEMORY;
27625  }
27626 
27627  resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
27628  if (resultALSA < 0) {
27629  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27630  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27631  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
27632  return ma_result_from_errno(-resultALSA);
27633  }
27634 
27635  /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
27636  isUsingMMap = MA_FALSE;
27637 #if 0 /* NOTE: MMAP mode temporarily disabled. */
27638  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. */
27639  if (!pConfig->alsa.noMMap) {
27640  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) {
27641  pDevice->alsa.isUsingMMap = MA_TRUE;
27642  }
27643  }
27644  }
27645 #endif
27646 
27647  if (!isUsingMMap) {
27648  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);
27649  if (resultALSA < 0) {
27650  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27651  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27652  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.");
27653  return ma_result_from_errno(-resultALSA);
27654  }
27655  }
27656 
27657  /*
27658  Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
27659  find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
27660  */
27661 
27662  /* Format. */
27663  {
27664  /*
27665  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
27666  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.
27667  */
27668  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) {
27669  /* We're either requesting the native format or the specified format is not supported. */
27670  size_t iFormat;
27671 
27672  formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
27673  for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
27674  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) {
27675  formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]);
27676  break;
27677  }
27678  }
27679 
27680  if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
27681  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27682  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27683  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.");
27684  return MA_FORMAT_NOT_SUPPORTED;
27685  }
27686  }
27687 
27688  resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
27689  if (resultALSA < 0) {
27690  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27691  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27692  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.");
27693  return ma_result_from_errno(-resultALSA);
27694  }
27695 
27696  internalFormat = ma_format_from_alsa(formatALSA);
27697  if (internalFormat == ma_format_unknown) {
27698  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27699  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27700  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.");
27701  return MA_FORMAT_NOT_SUPPORTED;
27702  }
27703  }
27704 
27705  /* Channels. */
27706  {
27707  unsigned int channels = pDescriptor->channels;
27708  if (channels == 0) {
27709  channels = MA_DEFAULT_CHANNELS;
27710  }
27711 
27712  resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
27713  if (resultALSA < 0) {
27714  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27715  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27716  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.");
27717  return ma_result_from_errno(-resultALSA);
27718  }
27719 
27720  internalChannels = (ma_uint32)channels;
27721  }
27722 
27723  /* Sample Rate */
27724  {
27725  unsigned int sampleRate;
27726 
27727  /*
27728  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
27729  problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
27730  resampling.
27731 
27732  To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
27733  sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
27734  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
27735  faster rate.
27736 
27737  miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
27738  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
27739  good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
27740 
27741  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
27742  this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
27743  */
27744  ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
27745 
27746  sampleRate = pDescriptor->sampleRate;
27747  if (sampleRate == 0) {
27748  sampleRate = MA_DEFAULT_SAMPLE_RATE;
27749  }
27750 
27751  resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
27752  if (resultALSA < 0) {
27753  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27754  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27755  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.");
27756  return ma_result_from_errno(-resultALSA);
27757  }
27758 
27759  internalSampleRate = (ma_uint32)sampleRate;
27760  }
27761 
27762  /* Periods. */
27763  {
27764  ma_uint32 periods = pDescriptor->periodCount;
27765 
27766  resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
27767  if (resultALSA < 0) {
27768  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27769  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27770  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.");
27771  return ma_result_from_errno(-resultALSA);
27772  }
27773 
27774  internalPeriods = periods;
27775  }
27776 
27777  /* Buffer Size */
27778  {
27779  ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods;
27780 
27781  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);
27782  if (resultALSA < 0) {
27783  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27784  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27785  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.");
27786  return ma_result_from_errno(-resultALSA);
27787  }
27788 
27789  internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
27790  }
27791 
27792  /* Apply hardware parameters. */
27793  resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
27794  if (resultALSA < 0) {
27795  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27796  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27797  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.");
27798  return ma_result_from_errno(-resultALSA);
27799  }
27800 
27801  ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27802  pHWParams = NULL;
27803 
27804 
27805  /* Software parameters. */
27806  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);
27807  if (pSWParams == NULL) {
27808  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27809  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters.");
27810  return MA_OUT_OF_MEMORY;
27811  }
27812 
27813  resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);
27814  if (resultALSA < 0) {
27815  ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27816  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27817  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.");
27818  return ma_result_from_errno(-resultALSA);
27819  }
27820 
27821  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));
27822  if (resultALSA < 0) {
27823  ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27824  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27825  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.");
27826  return ma_result_from_errno(-resultALSA);
27827  }
27828 
27829  resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
27830  if (resultALSA < 0) {
27831  bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
27832  }
27833 
27834  if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
27835  /*
27836  Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
27837  the size of a period. But for full-duplex we need to set it such that it is at least two periods.
27838  */
27839  resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
27840  if (resultALSA < 0) {
27841  ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27842  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27843  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.");
27844  return ma_result_from_errno(-resultALSA);
27845  }
27846 
27847  resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
27848  if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
27849  ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27850  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27851  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.");
27852  return ma_result_from_errno(-resultALSA);
27853  }
27854  }
27855 
27856  resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
27857  if (resultALSA < 0) {
27858  ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27859  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27860  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.");
27861  return ma_result_from_errno(-resultALSA);
27862  }
27863 
27864  ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27865  pSWParams = NULL;
27866 
27867 
27868  /* 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. */
27869  {
27870  ma_snd_pcm_chmap_t* pChmap = NULL;
27871  if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) {
27872  pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);
27873  }
27874 
27875  if (pChmap != NULL) {
27876  ma_uint32 iChannel;
27877 
27878  /* 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(). */
27879  if (pChmap->channels >= internalChannels) {
27880  /* Drop excess channels. */
27881  for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
27882  internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
27883  }
27884  } else {
27885  ma_uint32 i;
27886 
27887  /*
27888  Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
27889  channels. If validation fails, fall back to defaults.
27890  */
27891  ma_bool32 isValid = MA_TRUE;
27892 
27893  /* Fill with defaults. */
27894  ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
27895 
27896  /* Overwrite first pChmap->channels channels. */
27897  for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
27898  internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
27899  }
27900 
27901  /* Validate. */
27902  for (i = 0; i < internalChannels && isValid; ++i) {
27903  ma_uint32 j;
27904  for (j = i+1; j < internalChannels; ++j) {
27905  if (internalChannelMap[i] == internalChannelMap[j]) {
27906  isValid = MA_FALSE;
27907  break;
27908  }
27909  }
27910  }
27911 
27912  /* If our channel map is invalid, fall back to defaults. */
27913  if (!isValid) {
27914  ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
27915  }
27916  }
27917 
27918  free(pChmap);
27919  pChmap = NULL;
27920  } else {
27921  /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
27922  ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
27923  }
27924  }
27925 
27926 
27927  /*
27928  We need to retrieve the poll descriptors so we can use poll() to wait for data to become
27929  available for reading or writing. There's no well defined maximum for this so we're just going
27930  to allocate this on the heap.
27931  */
27932  pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);
27933  if (pollDescriptorCount <= 0) {
27934  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27935  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.");
27936  return MA_ERROR;
27937  }
27938 
27939  pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */
27940  if (pPollDescriptors == NULL) {
27941  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27942  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.");
27943  return MA_OUT_OF_MEMORY;
27944  }
27945 
27946  /*
27947  We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver
27948  never returns from writei() and readi(). This has been observed with the "pulse" device.
27949  */
27950  wakeupfd = eventfd(0, 0);
27951  if (wakeupfd < 0) {
27952  ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
27953  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27954  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.");
27955  return ma_result_from_errno(errno);
27956  }
27957 
27958  /* We'll place the wakeup fd at the start of the buffer. */
27959  pPollDescriptors[0].fd = wakeupfd;
27960  pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */
27961  pPollDescriptors[0].revents = 0;
27962 
27963  /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */
27964  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. */
27965  if (pollDescriptorCount <= 0) {
27966  close(wakeupfd);
27967  ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
27968  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27969  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors.");
27970  return MA_ERROR;
27971  }
27972 
27973  if (deviceType == ma_device_type_capture) {
27974  pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount;
27975  pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors;
27976  pDevice->alsa.wakeupfdCapture = wakeupfd;
27977  } else {
27978  pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount;
27979  pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors;
27980  pDevice->alsa.wakeupfdPlayback = wakeupfd;
27981  }
27982 
27983 
27984  /* We're done. Prepare the device. */
27985  resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
27986  if (resultALSA < 0) {
27987  close(wakeupfd);
27988  ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
27989  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27990  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.");
27991  return ma_result_from_errno(-resultALSA);
27992  }
27993 
27994 
27995  if (deviceType == ma_device_type_capture) {
27996  pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
27997  pDevice->alsa.isUsingMMapCapture = isUsingMMap;
27998  } else {
27999  pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
28000  pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
28001  }
28002 
28003  pDescriptor->format = internalFormat;
28004  pDescriptor->channels = internalChannels;
28005  pDescriptor->sampleRate = internalSampleRate;
28006  ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
28007  pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
28008  pDescriptor->periodCount = internalPeriods;
28009 
28010  return MA_SUCCESS;
28011 }
28012 
28013 static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
28014 {
28015  MA_ASSERT(pDevice != NULL);
28016 
28017  MA_ZERO_OBJECT(&pDevice->alsa);
28018 
28019  if (pConfig->deviceType == ma_device_type_loopback) {
28020  return MA_DEVICE_TYPE_NOT_SUPPORTED;
28021  }
28022 
28023  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
28024  ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
28025  if (result != MA_SUCCESS) {
28026  return result;
28027  }
28028  }
28029 
28030  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
28031  ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
28032  if (result != MA_SUCCESS) {
28033  return result;
28034  }
28035  }
28036 
28037  return MA_SUCCESS;
28038 }
28039 
28040 static ma_result ma_device_start__alsa(ma_device* pDevice)
28041 {
28042  int resultALSA;
28043 
28044  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28045  resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
28046  if (resultALSA < 0) {
28047  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.");
28048  return ma_result_from_errno(-resultALSA);
28049  }
28050  }
28051 
28052  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28053  /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */
28054  }
28055 
28056  return MA_SUCCESS;
28057 }
28058 
28059 static ma_result ma_device_stop__alsa(ma_device* pDevice)
28060 {
28061  /*
28062  The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is
28063  a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable.
28064  */
28065  int resultPoll;
28066 
28067  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28068  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n");
28069  ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
28070  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n");
28071 
28072  /* We need to prepare the device again, otherwise we won't be able to restart the device. */
28073  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n");
28074  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
28075  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n");
28076  } else {
28077  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n");
28078  }
28079 
28080  /* Clear the wakeupfd. */
28081  resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0);
28082  if (resultPoll > 0) {
28083  ma_uint64 t;
28084  read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t));
28085  }
28086  }
28087 
28088  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28089  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n");
28090  ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
28091  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n");
28092 
28093  /* We need to prepare the device again, otherwise we won't be able to restart the device. */
28094  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n");
28095  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
28096  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n");
28097  } else {
28098  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n");
28099  }
28100 
28101  /* Clear the wakeupfd. */
28102  resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0);
28103  if (resultPoll > 0) {
28104  ma_uint64 t;
28105  read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t));
28106  }
28107 
28108  }
28109 
28110  return MA_SUCCESS;
28111 }
28112 
28113 static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent)
28114 {
28115  for (;;) {
28116  unsigned short revents;
28117  int resultALSA;
28118  int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);
28119  if (resultPoll < 0) {
28120  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.\n");
28121  return ma_result_from_errno(errno);
28122  }
28123 
28124  /*
28125  Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor
28126  has had it's POLLIN flag set. If so, we need to actually read the data and then exit
28127  function. The wakeup descriptor will be the first item in the descriptors buffer.
28128  */
28129  if ((pPollDescriptors[0].revents & POLLIN) != 0) {
28130  ma_uint64 t;
28131  int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */
28132  if (resultRead < 0) {
28133  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n");
28134  return ma_result_from_errno(errno);
28135  }
28136 
28137  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n");
28138  return MA_DEVICE_NOT_STARTED;
28139  }
28140 
28141  /*
28142  Getting here means that some data should be able to be read. We need to use ALSA to
28143  translate the revents flags for us.
28144  */
28145  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. */
28146  if (resultALSA < 0) {
28147  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n");
28148  return ma_result_from_errno(-resultALSA);
28149  }
28150 
28151  if ((revents & POLLERR) != 0) {
28152  ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM);
28153  if (state == MA_SND_PCM_STATE_XRUN) {
28154  /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */
28155  } else {
28156  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));
28157  }
28158  }
28159 
28160  if ((revents & requiredEvent) == requiredEvent) {
28161  break; /* We're done. Data available for reading or writing. */
28162  }
28163  }
28164 
28165  return MA_SUCCESS;
28166 }
28167 
28168 static ma_result ma_device_wait_read__alsa(ma_device* pDevice)
28169 {
28170  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. */
28171 }
28172 
28173 static ma_result ma_device_wait_write__alsa(ma_device* pDevice)
28174 {
28175  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. */
28176 }
28177 
28178 static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
28179 {
28180  ma_snd_pcm_sframes_t resultALSA = 0;
28181 
28182  MA_ASSERT(pDevice != NULL);
28183  MA_ASSERT(pFramesOut != NULL);
28184 
28185  if (pFramesRead != NULL) {
28186  *pFramesRead = 0;
28187  }
28188 
28189  while (ma_device_get_state(pDevice) == ma_device_state_started) {
28190  ma_result result;
28191 
28192  /* 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. */
28193  result = ma_device_wait_read__alsa(pDevice);
28194  if (result != MA_SUCCESS) {
28195  return result;
28196  }
28197 
28198  /* Getting here means we should have data available. */
28199  resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
28200  if (resultALSA >= 0) {
28201  break; /* Success. */
28202  } else {
28203  if (resultALSA == -EAGAIN) {
28204  /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/
28205  continue; /* Try again. */
28206  } else if (resultALSA == -EPIPE) {
28207  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n");
28208 
28209  /* Overrun. Recover and try again. If this fails we need to return an error. */
28210  resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
28211  if (resultALSA < 0) {
28212  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.");
28213  return ma_result_from_errno((int)-resultALSA);
28214  }
28215 
28216  resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
28217  if (resultALSA < 0) {
28218  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
28219  return ma_result_from_errno((int)-resultALSA);
28220  }
28221 
28222  continue; /* Try reading again. */
28223  }
28224  }
28225  }
28226 
28227  if (pFramesRead != NULL) {
28228  *pFramesRead = resultALSA;
28229  }
28230 
28231  return MA_SUCCESS;
28232 }
28233 
28234 static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
28235 {
28236  ma_snd_pcm_sframes_t resultALSA = 0;
28237 
28238  MA_ASSERT(pDevice != NULL);
28239  MA_ASSERT(pFrames != NULL);
28240 
28241  if (pFramesWritten != NULL) {
28242  *pFramesWritten = 0;
28243  }
28244 
28245  while (ma_device_get_state(pDevice) == ma_device_state_started) {
28246  ma_result result;
28247 
28248  /* 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. */
28249  result = ma_device_wait_write__alsa(pDevice);
28250  if (result != MA_SUCCESS) {
28251  return result;
28252  }
28253 
28254  resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
28255  if (resultALSA >= 0) {
28256  break; /* Success. */
28257  } else {
28258  if (resultALSA == -EAGAIN) {
28259  /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/
28260  continue; /* Try again. */
28261  } else if (resultALSA == -EPIPE) {
28262  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n");
28263 
28264  /* Underrun. Recover and try again. If this fails we need to return an error. */
28265  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). */
28266  if (resultALSA < 0) {
28267  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.");
28268  return ma_result_from_errno((int)-resultALSA);
28269  }
28270 
28271  /*
28272  In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
28273  up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
28274  frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
28275  if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
28276  quite right here.
28277  */
28278  resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
28279  if (resultALSA < 0) {
28280  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
28281  return ma_result_from_errno((int)-resultALSA);
28282  }
28283 
28284  continue; /* Try writing again. */
28285  }
28286  }
28287  }
28288 
28289  if (pFramesWritten != NULL) {
28290  *pFramesWritten = resultALSA;
28291  }
28292 
28293  return MA_SUCCESS;
28294 }
28295 
28296 static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice)
28297 {
28298  ma_uint64 t = 1;
28299  int resultWrite = 0;
28300 
28301  MA_ASSERT(pDevice != NULL);
28302 
28303  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n");
28304 
28305  /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */
28306  if (pDevice->alsa.pPollDescriptorsCapture != NULL) {
28307  resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t));
28308  }
28309  if (pDevice->alsa.pPollDescriptorsPlayback != NULL) {
28310  resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t));
28311  }
28312 
28313  if (resultWrite < 0) {
28314  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n");
28315  return ma_result_from_errno(errno);
28316  }
28317 
28318  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n");
28319 
28320  return MA_SUCCESS;
28321 }
28322 
28323 static ma_result ma_context_uninit__alsa(ma_context* pContext)
28324 {
28325  MA_ASSERT(pContext != NULL);
28326  MA_ASSERT(pContext->backend == ma_backend_alsa);
28327 
28328  /* Clean up memory for memory leak checkers. */
28329  ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
28330 
28331 #ifndef MA_NO_RUNTIME_LINKING
28332  ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO);
28333 #endif
28334 
28335  ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
28336 
28337  return MA_SUCCESS;
28338 }
28339 
28340 static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
28341 {
28342  ma_result result;
28343 #ifndef MA_NO_RUNTIME_LINKING
28344  const char* libasoundNames[] = {
28345  "libasound.so.2",
28346  "libasound.so"
28347  };
28348  size_t i;
28349 
28350  for (i = 0; i < ma_countof(libasoundNames); ++i) {
28351  pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]);
28352  if (pContext->alsa.asoundSO != NULL) {
28353  break;
28354  }
28355  }
28356 
28357  if (pContext->alsa.asoundSO == NULL) {
28358  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n");
28359  return MA_NO_BACKEND;
28360  }
28361 
28362  pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open");
28363  pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close");
28364  pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
28365  pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
28366  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");
28367  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");
28368  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");
28369  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");
28370  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");
28371  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");
28372  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");
28373  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");
28374  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");
28375  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");
28376  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");
28377  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");
28378  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");
28379  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");
28380  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");
28381  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");
28382  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");
28383  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");
28384  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");
28385  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");
28386  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");
28387  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");
28388  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");
28389  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");
28390  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");
28391  pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params");
28392  pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
28393  pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
28394  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");
28395  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");
28396  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");
28397  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");
28398  pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params");
28399  pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
28400  pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
28401  pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap");
28402  pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state");
28403  pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare");
28404  pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start");
28405  pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop");
28406  pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain");
28407  pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset");
28408  pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint");
28409  pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint");
28410  pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index");
28411  pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint");
28412  pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
28413  pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
28414  pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover");
28415  pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi");
28416  pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei");
28417  pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail");
28418  pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update");
28419  pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait");
28420  pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock");
28421  pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info");
28422  pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
28423  pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name");
28424  pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors");
28425  pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count");
28426  pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents");
28427  pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global");
28428 #else
28429  /* The system below is just for type safety. */
28430  ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
28431  ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
28432  ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
28433  ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
28434  ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
28435  ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
28436  ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
28437  ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels;
28438  ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
28439  ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
28440  ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate;
28441  ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
28442  ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax;
28443  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;
28444  ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
28445  ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
28446  ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
28447  ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
28448  ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
28449  ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
28450  ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
28451  ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
28452  ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
28453  ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
28454  ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
28455  ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
28456  ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format;
28457  ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels;
28458  ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate;
28459  ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
28460  ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
28461  ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
28462  ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
28463  ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
28464  ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
28465  ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
28466  ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
28467  ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
28468  ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
28469  ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
28470  ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
28471  ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
28472  ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
28473  ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
28474  ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
28475  ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset;
28476  ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
28477  ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
28478  ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
28479  ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
28480  ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
28481  ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
28482  ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
28483  ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
28484  ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
28485  ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
28486  ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
28487  ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
28488  ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock;
28489  ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
28490  ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
28491  ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
28492  ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors;
28493  ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count;
28494  ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents;
28495  ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
28496 
28497  pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
28498  pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
28499  pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
28500  pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
28501  pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
28502  pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
28503  pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
28504  pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels;
28505  pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
28506  pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax;
28507  pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
28508  pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate;
28509  pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
28510  pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
28511  pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
28512  pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
28513  pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
28514  pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
28515  pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
28516  pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
28517  pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
28518  pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min;
28519  pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max;
28520  pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
28521  pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
28522  pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
28523  pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format;
28524  pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels;
28525  pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate;
28526  pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
28527  pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
28528  pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
28529  pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
28530  pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
28531  pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
28532  pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
28533  pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
28534  pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
28535  pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
28536  pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
28537  pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
28538  pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
28539  pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
28540  pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
28541  pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
28542  pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset;
28543  pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
28544  pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
28545  pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
28546  pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
28547  pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
28548  pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
28549  pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
28550  pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
28551  pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
28552  pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
28553  pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
28554  pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
28555  pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock;
28556  pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
28557  pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
28558  pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
28559  pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors;
28560  pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count;
28561  pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents;
28562  pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
28563 #endif
28564 
28565  pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
28566 
28567  result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock);
28568  if (result != MA_SUCCESS) {
28569  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.");
28570  return result;
28571  }
28572 
28573  pCallbacks->onContextInit = ma_context_init__alsa;
28574  pCallbacks->onContextUninit = ma_context_uninit__alsa;
28575  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;
28576  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa;
28577  pCallbacks->onDeviceInit = ma_device_init__alsa;
28578  pCallbacks->onDeviceUninit = ma_device_uninit__alsa;
28579  pCallbacks->onDeviceStart = ma_device_start__alsa;
28580  pCallbacks->onDeviceStop = ma_device_stop__alsa;
28581  pCallbacks->onDeviceRead = ma_device_read__alsa;
28582  pCallbacks->onDeviceWrite = ma_device_write__alsa;
28583  pCallbacks->onDeviceDataLoop = NULL;
28584  pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
28585 
28586  return MA_SUCCESS;
28587 }
28588 #endif /* ALSA */
28589 
28590 
28591 
28592 /******************************************************************************
28593 
28594 PulseAudio Backend
28595 
28596 ******************************************************************************/
28597 #ifdef MA_HAS_PULSEAUDIO
28598 /*
28599 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
28600 in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.
28601 
28602 PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it
28603 allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it
28604 appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or
28605 write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the
28606 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
28607 when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API.
28608 
28609 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
28610 get fun, and I don't mean that in a good way...
28611 
28612 The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands
28613 don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is
28614 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
28615 all of PulseAudio's problems stem from.
28616 
28617 When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own
28618 vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called
28619 pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop
28620 because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use
28621 it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed.
28622 
28623 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
28624 to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded
28625 main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely
28626 specialized such as if you want to integrate it into your application's existing main loop infrastructure.
28627 
28628 (EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262.
28629 It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.)
28630 
28631 Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to
28632 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
28633 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
28634 is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if
28635 you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()`
28636 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
28637 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.
28638 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.
28639 This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before
28640 attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.
28641 
28642 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
28643 internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the
28644 host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.
28645 
28646 Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.
28647 The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call
28648 `pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get
28649 information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object
28650 is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to
28651 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
28652 context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.
28653 All of that just to retrieve basic information about a device!
28654 
28655 Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the
28656 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
28657 choices in PulseAudio.
28658 
28659 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
28660 because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for
28661 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
28662 set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
28663 straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
28664 PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)
28665 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
28666 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
28667 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
28668 that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the
28669 stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data
28670 callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio
28671 doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been
28672 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
28673 callback is not fired.
28674 
28675 This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will
28676 continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device
28677 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
28678 PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call
28679 `pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always
28680 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
28681 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
28682 *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
28683 important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained
28684 before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write
28685 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!
28686 
28687 This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*
28688 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
28689 resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This
28690 disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the
28691 callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)
28692 
28693 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,
28694 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
28695 "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
28696 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
28697 guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is
28698 absolutely beyond me. Would it really be that hard to just make it run synchronously?
28699 
28700 Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that
28701 they were initialized in.
28702 
28703 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
28704 embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to
28705 run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche
28706 requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is
28707 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
28708 parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These
28709 changes alone will change PulseAudio from one of the worst audio APIs to one of the best.
28710 */
28711 
28712 
28713 /*
28714 It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header
28715 to check for type safety. We cannot do this when linking at run time because the header might not be available.
28716 */
28717 #ifdef MA_NO_RUNTIME_LINKING
28718 
28719 /* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
28720 #if !defined(__cplusplus)
28721  #if defined(__STRICT_ANSI__)
28722  #if !defined(inline)
28723  #define inline __inline__ __attribute__((always_inline))
28724  #define MA_INLINE_DEFINED
28725  #endif
28726  #endif
28727 #endif
28728 #include <pulse/pulseaudio.h>
28729 #if defined(MA_INLINE_DEFINED)
28730  #undef inline
28731  #undef MA_INLINE_DEFINED
28732 #endif
28733 
28734 #define MA_PA_OK PA_OK
28735 #define MA_PA_ERR_ACCESS PA_ERR_ACCESS
28736 #define MA_PA_ERR_INVALID PA_ERR_INVALID
28737 #define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
28738 #define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
28739 
28740 #define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
28741 #define MA_PA_RATE_MAX PA_RATE_MAX
28742 
28743 typedef pa_context_flags_t ma_pa_context_flags_t;
28744 #define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
28745 #define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
28746 #define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
28747 
28748 typedef pa_stream_flags_t ma_pa_stream_flags_t;
28749 #define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
28750 #define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
28751 #define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
28752 #define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
28753 #define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
28754 #define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
28755 #define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
28756 #define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
28757 #define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
28758 #define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
28759 #define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
28760 #define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
28761 #define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
28762 #define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
28763 #define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
28764 #define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
28765 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
28766 #define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
28767 #define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
28768 #define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
28769 #define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
28770 
28771 typedef pa_sink_flags_t ma_pa_sink_flags_t;
28772 #define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
28773 #define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
28774 #define MA_PA_SINK_LATENCY PA_SINK_LATENCY
28775 #define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
28776 #define MA_PA_SINK_NETWORK PA_SINK_NETWORK
28777 #define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
28778 #define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
28779 #define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
28780 #define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
28781 #define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
28782 
28783 typedef pa_source_flags_t ma_pa_source_flags_t;
28784 #define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
28785 #define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
28786 #define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
28787 #define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
28788 #define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
28789 #define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
28790 #define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
28791 #define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
28792 #define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
28793 
28794 typedef pa_context_state_t ma_pa_context_state_t;
28795 #define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
28796 #define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
28797 #define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
28798 #define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
28799 #define MA_PA_CONTEXT_READY PA_CONTEXT_READY
28800 #define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
28801 #define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
28802 
28803 typedef pa_stream_state_t ma_pa_stream_state_t;
28804 #define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
28805 #define MA_PA_STREAM_CREATING PA_STREAM_CREATING
28806 #define MA_PA_STREAM_READY PA_STREAM_READY
28807 #define MA_PA_STREAM_FAILED PA_STREAM_FAILED
28808 #define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
28809 
28810 typedef pa_operation_state_t ma_pa_operation_state_t;
28811 #define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
28812 #define MA_PA_OPERATION_DONE PA_OPERATION_DONE
28813 #define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
28814 
28815 typedef pa_sink_state_t ma_pa_sink_state_t;
28816 #define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
28817 #define MA_PA_SINK_RUNNING PA_SINK_RUNNING
28818 #define MA_PA_SINK_IDLE PA_SINK_IDLE
28819 #define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
28820 
28821 typedef pa_source_state_t ma_pa_source_state_t;
28822 #define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
28823 #define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
28824 #define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
28825 #define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
28826 
28827 typedef pa_seek_mode_t ma_pa_seek_mode_t;
28828 #define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
28829 #define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
28830 #define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
28831 #define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
28832 
28833 typedef pa_channel_position_t ma_pa_channel_position_t;
28834 #define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
28835 #define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
28836 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
28837 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
28838 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
28839 #define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
28840 #define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
28841 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
28842 #define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
28843 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
28844 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
28845 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
28846 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
28847 #define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
28848 #define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
28849 #define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
28850 #define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
28851 #define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
28852 #define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
28853 #define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
28854 #define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
28855 #define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
28856 #define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
28857 #define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
28858 #define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
28859 #define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
28860 #define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
28861 #define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
28862 #define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
28863 #define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
28864 #define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
28865 #define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
28866 #define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
28867 #define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
28868 #define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
28869 #define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
28870 #define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
28871 #define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
28872 #define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
28873 #define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
28874 #define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
28875 #define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
28876 #define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
28877 #define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
28878 #define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
28879 #define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
28880 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
28881 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
28882 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
28883 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
28884 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
28885 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
28886 #define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
28887 #define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
28888 #define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
28889 #define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
28890 
28891 typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
28892 #define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
28893 #define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
28894 #define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
28895 #define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
28896 #define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
28897 #define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
28898 
28899 typedef pa_sample_format_t ma_pa_sample_format_t;
28900 #define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
28901 #define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
28902 #define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
28903 #define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
28904 #define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
28905 #define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
28906 #define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
28907 #define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
28908 #define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
28909 #define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
28910 #define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
28911 #define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
28912 #define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
28913 #define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
28914 
28915 typedef pa_mainloop ma_pa_mainloop;
28916 typedef pa_threaded_mainloop ma_pa_threaded_mainloop;
28917 typedef pa_mainloop_api ma_pa_mainloop_api;
28918 typedef pa_context ma_pa_context;
28919 typedef pa_operation ma_pa_operation;
28920 typedef pa_stream ma_pa_stream;
28921 typedef pa_spawn_api ma_pa_spawn_api;
28922 typedef pa_buffer_attr ma_pa_buffer_attr;
28923 typedef pa_channel_map ma_pa_channel_map;
28924 typedef pa_cvolume ma_pa_cvolume;
28925 typedef pa_sample_spec ma_pa_sample_spec;
28926 typedef pa_sink_info ma_pa_sink_info;
28927 typedef pa_source_info ma_pa_source_info;
28928 
28929 typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
28930 typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
28931 typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
28932 typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
28933 typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
28934 typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t;
28935 typedef pa_free_cb_t ma_pa_free_cb_t;
28936 #else
28937 #define MA_PA_OK 0
28938 #define MA_PA_ERR_ACCESS 1
28939 #define MA_PA_ERR_INVALID 2
28940 #define MA_PA_ERR_NOENTITY 5
28941 #define MA_PA_ERR_NOTSUPPORTED 19
28942 
28943 #define MA_PA_CHANNELS_MAX 32
28944 #define MA_PA_RATE_MAX 384000
28945 
28946 typedef int ma_pa_context_flags_t;
28947 #define MA_PA_CONTEXT_NOFLAGS 0x00000000
28948 #define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
28949 #define MA_PA_CONTEXT_NOFAIL 0x00000002
28950 
28951 typedef int ma_pa_stream_flags_t;
28952 #define MA_PA_STREAM_NOFLAGS 0x00000000
28953 #define MA_PA_STREAM_START_CORKED 0x00000001
28954 #define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
28955 #define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
28956 #define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
28957 #define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
28958 #define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
28959 #define MA_PA_STREAM_FIX_FORMAT 0x00000040
28960 #define MA_PA_STREAM_FIX_RATE 0x00000080
28961 #define MA_PA_STREAM_FIX_CHANNELS 0x00000100
28962 #define MA_PA_STREAM_DONT_MOVE 0x00000200
28963 #define MA_PA_STREAM_VARIABLE_RATE 0x00000400
28964 #define MA_PA_STREAM_PEAK_DETECT 0x00000800
28965 #define MA_PA_STREAM_START_MUTED 0x00001000
28966 #define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
28967 #define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
28968 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
28969 #define MA_PA_STREAM_START_UNMUTED 0x00010000
28970 #define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
28971 #define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
28972 #define MA_PA_STREAM_PASSTHROUGH 0x00080000
28973 
28974 typedef int ma_pa_sink_flags_t;
28975 #define MA_PA_SINK_NOFLAGS 0x00000000
28976 #define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
28977 #define MA_PA_SINK_LATENCY 0x00000002
28978 #define MA_PA_SINK_HARDWARE 0x00000004
28979 #define MA_PA_SINK_NETWORK 0x00000008
28980 #define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
28981 #define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
28982 #define MA_PA_SINK_FLAT_VOLUME 0x00000040
28983 #define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
28984 #define MA_PA_SINK_SET_FORMATS 0x00000100
28985 
28986 typedef int ma_pa_source_flags_t;
28987 #define MA_PA_SOURCE_NOFLAGS 0x00000000
28988 #define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
28989 #define MA_PA_SOURCE_LATENCY 0x00000002
28990 #define MA_PA_SOURCE_HARDWARE 0x00000004
28991 #define MA_PA_SOURCE_NETWORK 0x00000008
28992 #define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
28993 #define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
28994 #define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
28995 #define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
28996 
28997 typedef int ma_pa_context_state_t;
28998 #define MA_PA_CONTEXT_UNCONNECTED 0
28999 #define MA_PA_CONTEXT_CONNECTING 1
29000 #define MA_PA_CONTEXT_AUTHORIZING 2
29001 #define MA_PA_CONTEXT_SETTING_NAME 3
29002 #define MA_PA_CONTEXT_READY 4
29003 #define MA_PA_CONTEXT_FAILED 5
29004 #define MA_PA_CONTEXT_TERMINATED 6
29005 
29006 typedef int ma_pa_stream_state_t;
29007 #define MA_PA_STREAM_UNCONNECTED 0
29008 #define MA_PA_STREAM_CREATING 1
29009 #define MA_PA_STREAM_READY 2
29010 #define MA_PA_STREAM_FAILED 3
29011 #define MA_PA_STREAM_TERMINATED 4
29012 
29013 typedef int ma_pa_operation_state_t;
29014 #define MA_PA_OPERATION_RUNNING 0
29015 #define MA_PA_OPERATION_DONE 1
29016 #define MA_PA_OPERATION_CANCELLED 2
29017 
29018 typedef int ma_pa_sink_state_t;
29019 #define MA_PA_SINK_INVALID_STATE -1
29020 #define MA_PA_SINK_RUNNING 0
29021 #define MA_PA_SINK_IDLE 1
29022 #define MA_PA_SINK_SUSPENDED 2
29023 
29024 typedef int ma_pa_source_state_t;
29025 #define MA_PA_SOURCE_INVALID_STATE -1
29026 #define MA_PA_SOURCE_RUNNING 0
29027 #define MA_PA_SOURCE_IDLE 1
29028 #define MA_PA_SOURCE_SUSPENDED 2
29029 
29030 typedef int ma_pa_seek_mode_t;
29031 #define MA_PA_SEEK_RELATIVE 0
29032 #define MA_PA_SEEK_ABSOLUTE 1
29033 #define MA_PA_SEEK_RELATIVE_ON_READ 2
29034 #define MA_PA_SEEK_RELATIVE_END 3
29035 
29036 typedef int ma_pa_channel_position_t;
29037 #define MA_PA_CHANNEL_POSITION_INVALID -1
29038 #define MA_PA_CHANNEL_POSITION_MONO 0
29039 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
29040 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
29041 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
29042 #define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
29043 #define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
29044 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
29045 #define MA_PA_CHANNEL_POSITION_LFE 7
29046 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
29047 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
29048 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
29049 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
29050 #define MA_PA_CHANNEL_POSITION_AUX0 12
29051 #define MA_PA_CHANNEL_POSITION_AUX1 13
29052 #define MA_PA_CHANNEL_POSITION_AUX2 14
29053 #define MA_PA_CHANNEL_POSITION_AUX3 15
29054 #define MA_PA_CHANNEL_POSITION_AUX4 16
29055 #define MA_PA_CHANNEL_POSITION_AUX5 17
29056 #define MA_PA_CHANNEL_POSITION_AUX6 18
29057 #define MA_PA_CHANNEL_POSITION_AUX7 19
29058 #define MA_PA_CHANNEL_POSITION_AUX8 20
29059 #define MA_PA_CHANNEL_POSITION_AUX9 21
29060 #define MA_PA_CHANNEL_POSITION_AUX10 22
29061 #define MA_PA_CHANNEL_POSITION_AUX11 23
29062 #define MA_PA_CHANNEL_POSITION_AUX12 24
29063 #define MA_PA_CHANNEL_POSITION_AUX13 25
29064 #define MA_PA_CHANNEL_POSITION_AUX14 26
29065 #define MA_PA_CHANNEL_POSITION_AUX15 27
29066 #define MA_PA_CHANNEL_POSITION_AUX16 28
29067 #define MA_PA_CHANNEL_POSITION_AUX17 29
29068 #define MA_PA_CHANNEL_POSITION_AUX18 30
29069 #define MA_PA_CHANNEL_POSITION_AUX19 31
29070 #define MA_PA_CHANNEL_POSITION_AUX20 32
29071 #define MA_PA_CHANNEL_POSITION_AUX21 33
29072 #define MA_PA_CHANNEL_POSITION_AUX22 34
29073 #define MA_PA_CHANNEL_POSITION_AUX23 35
29074 #define MA_PA_CHANNEL_POSITION_AUX24 36
29075 #define MA_PA_CHANNEL_POSITION_AUX25 37
29076 #define MA_PA_CHANNEL_POSITION_AUX26 38
29077 #define MA_PA_CHANNEL_POSITION_AUX27 39
29078 #define MA_PA_CHANNEL_POSITION_AUX28 40
29079 #define MA_PA_CHANNEL_POSITION_AUX29 41
29080 #define MA_PA_CHANNEL_POSITION_AUX30 42
29081 #define MA_PA_CHANNEL_POSITION_AUX31 43
29082 #define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
29083 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
29084 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
29085 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
29086 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
29087 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
29088 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
29089 #define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
29090 #define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
29091 #define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
29092 #define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
29093 
29094 typedef int ma_pa_channel_map_def_t;
29095 #define MA_PA_CHANNEL_MAP_AIFF 0
29096 #define MA_PA_CHANNEL_MAP_ALSA 1
29097 #define MA_PA_CHANNEL_MAP_AUX 2
29098 #define MA_PA_CHANNEL_MAP_WAVEEX 3
29099 #define MA_PA_CHANNEL_MAP_OSS 4
29100 #define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
29101 
29102 typedef int ma_pa_sample_format_t;
29103 #define MA_PA_SAMPLE_INVALID -1
29104 #define MA_PA_SAMPLE_U8 0
29105 #define MA_PA_SAMPLE_ALAW 1
29106 #define MA_PA_SAMPLE_ULAW 2
29107 #define MA_PA_SAMPLE_S16LE 3
29108 #define MA_PA_SAMPLE_S16BE 4
29109 #define MA_PA_SAMPLE_FLOAT32LE 5
29110 #define MA_PA_SAMPLE_FLOAT32BE 6
29111 #define MA_PA_SAMPLE_S32LE 7
29112 #define MA_PA_SAMPLE_S32BE 8
29113 #define MA_PA_SAMPLE_S24LE 9
29114 #define MA_PA_SAMPLE_S24BE 10
29115 #define MA_PA_SAMPLE_S24_32LE 11
29116 #define MA_PA_SAMPLE_S24_32BE 12
29117 
29118 typedef struct ma_pa_mainloop ma_pa_mainloop;
29119 typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop;
29120 typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
29121 typedef struct ma_pa_context ma_pa_context;
29122 typedef struct ma_pa_operation ma_pa_operation;
29123 typedef struct ma_pa_stream ma_pa_stream;
29124 typedef struct ma_pa_spawn_api ma_pa_spawn_api;
29125 
29126 typedef struct
29127 {
29128  ma_uint32 maxlength;
29129  ma_uint32 tlength;
29130  ma_uint32 prebuf;
29131  ma_uint32 minreq;
29132  ma_uint32 fragsize;
29133 } ma_pa_buffer_attr;
29134 
29135 typedef struct
29136 {
29137  ma_uint8 channels;
29138  ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
29139 } ma_pa_channel_map;
29140 
29141 typedef struct
29142 {
29143  ma_uint8 channels;
29144  ma_uint32 values[MA_PA_CHANNELS_MAX];
29145 } ma_pa_cvolume;
29146 
29147 typedef struct
29148 {
29149  ma_pa_sample_format_t format;
29150  ma_uint32 rate;
29151  ma_uint8 channels;
29152 } ma_pa_sample_spec;
29153 
29154 typedef struct
29155 {
29156  const char* name;
29157  ma_uint32 index;
29158  const char* description;
29159  ma_pa_sample_spec sample_spec;
29160  ma_pa_channel_map channel_map;
29161  ma_uint32 owner_module;
29162  ma_pa_cvolume volume;
29163  int mute;
29164  ma_uint32 monitor_source;
29165  const char* monitor_source_name;
29166  ma_uint64 latency;
29167  const char* driver;
29168  ma_pa_sink_flags_t flags;
29169  void* proplist;
29170  ma_uint64 configured_latency;
29171  ma_uint32 base_volume;
29172  ma_pa_sink_state_t state;
29173  ma_uint32 n_volume_steps;
29174  ma_uint32 card;
29175  ma_uint32 n_ports;
29176  void** ports;
29177  void* active_port;
29178  ma_uint8 n_formats;
29179  void** formats;
29180 } ma_pa_sink_info;
29181 
29182 typedef struct
29183 {
29184  const char *name;
29185  ma_uint32 index;
29186  const char *description;
29187  ma_pa_sample_spec sample_spec;
29188  ma_pa_channel_map channel_map;
29189  ma_uint32 owner_module;
29190  ma_pa_cvolume volume;
29191  int mute;
29192  ma_uint32 monitor_of_sink;
29193  const char *monitor_of_sink_name;
29194  ma_uint64 latency;
29195  const char *driver;
29196  ma_pa_source_flags_t flags;
29197  void* proplist;
29198  ma_uint64 configured_latency;
29199  ma_uint32 base_volume;
29200  ma_pa_source_state_t state;
29201  ma_uint32 n_volume_steps;
29202  ma_uint32 card;
29203  ma_uint32 n_ports;
29204  void** ports;
29205  void* active_port;
29206  ma_uint8 n_formats;
29207  void** formats;
29208 } ma_pa_source_info;
29209 
29210 typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
29211 typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
29212 typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
29213 typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
29214 typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
29215 typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata);
29216 typedef void (* ma_pa_free_cb_t) (void* p);
29217 #endif
29218 
29219 
29220 typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void);
29221 typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
29222 typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval);
29223 typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
29224 typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
29225 typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
29226 typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void);
29227 typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m);
29228 typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m);
29229 typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m);
29230 typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m);
29231 typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m);
29232 typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m);
29233 typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept);
29234 typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m);
29235 typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m);
29236 typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m);
29237 typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m);
29238 typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name);
29239 typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
29240 typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
29241 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);
29242 typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
29243 typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
29244 typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
29245 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);
29246 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);
29247 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);
29248 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);
29249 typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
29250 typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
29251 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);
29252 typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
29253 typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
29254 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);
29255 typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
29256 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);
29257 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);
29258 typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
29259 typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
29260 typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
29261 typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
29262 typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
29263 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);
29264 typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
29265 typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
29266 typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
29267 typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
29268 typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
29269 typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s);
29270 typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
29271 typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
29272 typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
29273 typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
29274 typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
29275 typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
29276 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);
29277 typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
29278 typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
29279 typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
29280 typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
29281 
29282 typedef struct
29283 {
29284  ma_uint32 count;
29285  ma_uint32 capacity;
29286  ma_device_info* pInfo;
29287 } ma_pulse_device_enum_data;
29288 
29289 static ma_result ma_result_from_pulse(int result)
29290 {
29291  if (result < 0) {
29292  return MA_ERROR;
29293  }
29294 
29295  switch (result) {
29296  case MA_PA_OK: return MA_SUCCESS;
29297  case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
29298  case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
29299  case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
29300  default: return MA_ERROR;
29301  }
29302 }
29303 
29304 #if 0
29305 static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
29306 {
29307  if (ma_is_little_endian()) {
29308  switch (format) {
29309  case ma_format_s16: return MA_PA_SAMPLE_S16LE;
29310  case ma_format_s24: return MA_PA_SAMPLE_S24LE;
29311  case ma_format_s32: return MA_PA_SAMPLE_S32LE;
29312  case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
29313  default: break;
29314  }
29315  } else {
29316  switch (format) {
29317  case ma_format_s16: return MA_PA_SAMPLE_S16BE;
29318  case ma_format_s24: return MA_PA_SAMPLE_S24BE;
29319  case ma_format_s32: return MA_PA_SAMPLE_S32BE;
29320  case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
29321  default: break;
29322  }
29323  }
29324 
29325  /* Endian agnostic. */
29326  switch (format) {
29327  case ma_format_u8: return MA_PA_SAMPLE_U8;
29328  default: return MA_PA_SAMPLE_INVALID;
29329  }
29330 }
29331 #endif
29332 
29333 static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
29334 {
29335  if (ma_is_little_endian()) {
29336  switch (format) {
29337  case MA_PA_SAMPLE_S16LE: return ma_format_s16;
29338  case MA_PA_SAMPLE_S24LE: return ma_format_s24;
29339  case MA_PA_SAMPLE_S32LE: return ma_format_s32;
29340  case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
29341  default: break;
29342  }
29343  } else {
29344  switch (format) {
29345  case MA_PA_SAMPLE_S16BE: return ma_format_s16;
29346  case MA_PA_SAMPLE_S24BE: return ma_format_s24;
29347  case MA_PA_SAMPLE_S32BE: return ma_format_s32;
29348  case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
29349  default: break;
29350  }
29351  }
29352 
29353  /* Endian agnostic. */
29354  switch (format) {
29355  case MA_PA_SAMPLE_U8: return ma_format_u8;
29356  default: return ma_format_unknown;
29357  }
29358 }
29359 
29360 static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
29361 {
29362  switch (position)
29363  {
29364  case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
29365  case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
29366  case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
29367  case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
29368  case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
29369  case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
29370  case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
29371  case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
29372  case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
29373  case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
29374  case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
29375  case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
29376  case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
29377  case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
29378  case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
29379  case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
29380  case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
29381  case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
29382  case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
29383  case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
29384  case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
29385  case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
29386  case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
29387  case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
29388  case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
29389  case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
29390  case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
29391  case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
29392  case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
29393  case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
29394  case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
29395  case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
29396  case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
29397  case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
29398  case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
29399  case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
29400  case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
29401  case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
29402  case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
29403  case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
29404  case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
29405  case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
29406  case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
29407  case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
29408  case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
29409  case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
29410  case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
29411  case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
29412  case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
29413  case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
29414  case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
29415  case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
29416  default: return MA_CHANNEL_NONE;
29417  }
29418 }
29419 
29420 #if 0
29421 static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
29422 {
29423  switch (position)
29424  {
29425  case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
29426  case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
29427  case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
29428  case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
29429  case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
29430  case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
29431  case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
29432  case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
29433  case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
29434  case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
29435  case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
29436  case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
29437  case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
29438  case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
29439  case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
29440  case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
29441  case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
29442  case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
29443  case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
29444  case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
29445  case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
29446  case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
29447  case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
29448  case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
29449  case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
29450  case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
29451  case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
29452  case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
29453  case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
29454  case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
29455  case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
29456  case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
29457  case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
29458  default: return (ma_pa_channel_position_t)position;
29459  }
29460 }
29461 #endif
29462 
29463 static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
29464 {
29465  int resultPA;
29466  ma_pa_operation_state_t state;
29467 
29468  MA_ASSERT(pContext != NULL);
29469  MA_ASSERT(pOP != NULL);
29470 
29471  for (;;) {
29472  state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP);
29473  if (state != MA_PA_OPERATION_RUNNING) {
29474  break; /* Done. */
29475  }
29476 
29477  resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
29478  if (resultPA < 0) {
29479  return ma_result_from_pulse(resultPA);
29480  }
29481  }
29482 
29483  return MA_SUCCESS;
29484 }
29485 
29486 static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
29487 {
29488  ma_result result;
29489 
29490  if (pOP == NULL) {
29491  return MA_INVALID_ARGS;
29492  }
29493 
29494  result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
29495  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
29496 
29497  return result;
29498 }
29499 
29500 static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext)
29501 {
29502  int resultPA;
29503  ma_pa_context_state_t state;
29504 
29505  for (;;) {
29506  state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext);
29507  if (state == MA_PA_CONTEXT_READY) {
29508  break; /* Done. */
29509  }
29510 
29511  if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
29512  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.");
29513  return MA_ERROR;
29514  }
29515 
29516  resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
29517  if (resultPA < 0) {
29518  return ma_result_from_pulse(resultPA);
29519  }
29520  }
29521 
29522  /* Should never get here. */
29523  return MA_SUCCESS;
29524 }
29525 
29526 static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream)
29527 {
29528  int resultPA;
29529  ma_pa_stream_state_t state;
29530 
29531  for (;;) {
29532  state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream);
29533  if (state == MA_PA_STREAM_READY) {
29534  break; /* Done. */
29535  }
29536 
29537  if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) {
29538  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream.");
29539  return MA_ERROR;
29540  }
29541 
29542  resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
29543  if (resultPA < 0) {
29544  return ma_result_from_pulse(resultPA);
29545  }
29546  }
29547 
29548  return MA_SUCCESS;
29549 }
29550 
29551 
29552 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)
29553 {
29554  ma_result result;
29555  ma_ptr pMainLoop;
29556  ma_ptr pPulseContext;
29557 
29558  MA_ASSERT(ppMainLoop != NULL);
29559  MA_ASSERT(ppPulseContext != NULL);
29560 
29561  /* 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. */
29562  pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
29563  if (pMainLoop == NULL) {
29564  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.");
29565  return MA_FAILED_TO_INIT_BACKEND;
29566  }
29567 
29568  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);
29569  if (pPulseContext == NULL) {
29570  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.");
29571  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
29572  return MA_FAILED_TO_INIT_BACKEND;
29573  }
29574 
29575  /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */
29576  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));
29577  if (result != MA_SUCCESS) {
29578  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.");
29579  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
29580  return result;
29581  }
29582 
29583  /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */
29584  result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext);
29585  if (result != MA_SUCCESS) {
29586  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed.");
29587  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
29588  return result;
29589  }
29590 
29591  *ppMainLoop = pMainLoop;
29592  *ppPulseContext = pPulseContext;
29593 
29594  return MA_SUCCESS;
29595 }
29596 
29597 
29598 static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
29599 {
29600  ma_pa_sink_info* pInfoOut;
29601 
29602  if (endOfList > 0) {
29603  return;
29604  }
29605 
29606  /*
29607  There has been a report that indicates that pInfo can be null which results
29608  in a null pointer dereference below. We'll check for this for safety.
29609  */
29610  if (pInfo == NULL) {
29611  return;
29612  }
29613 
29614  pInfoOut = (ma_pa_sink_info*)pUserData;
29615  MA_ASSERT(pInfoOut != NULL);
29616 
29617  *pInfoOut = *pInfo;
29618 
29619  (void)pPulseContext; /* Unused. */
29620 }
29621 
29622 static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
29623 {
29624  ma_pa_source_info* pInfoOut;
29625 
29626  if (endOfList > 0) {
29627  return;
29628  }
29629 
29630  /*
29631  There has been a report that indicates that pInfo can be null which results
29632  in a null pointer dereference below. We'll check for this for safety.
29633  */
29634  if (pInfo == NULL) {
29635  return;
29636  }
29637 
29638  pInfoOut = (ma_pa_source_info*)pUserData;
29639  MA_ASSERT(pInfoOut != NULL);
29640 
29641  *pInfoOut = *pInfo;
29642 
29643  (void)pPulseContext; /* Unused. */
29644 }
29645 
29646 #if 0
29647 static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
29648 {
29649  ma_device* pDevice;
29650 
29651  if (endOfList > 0) {
29652  return;
29653  }
29654 
29655  pDevice = (ma_device*)pUserData;
29656  MA_ASSERT(pDevice != NULL);
29657 
29658  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
29659 
29660  (void)pPulseContext; /* Unused. */
29661 }
29662 
29663 static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
29664 {
29665  ma_device* pDevice;
29666 
29667  if (endOfList > 0) {
29668  return;
29669  }
29670 
29671  pDevice = (ma_device*)pUserData;
29672  MA_ASSERT(pDevice != NULL);
29673 
29674  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
29675 
29676  (void)pPulseContext; /* Unused. */
29677 }
29678 #endif
29679 
29680 static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)
29681 {
29682  ma_pa_operation* pOP;
29683 
29684  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);
29685  if (pOP == NULL) {
29686  return MA_ERROR;
29687  }
29688 
29689  return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29690 }
29691 
29692 static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)
29693 {
29694  ma_pa_operation* pOP;
29695 
29696  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);
29697  if (pOP == NULL) {
29698  return MA_ERROR;
29699  }
29700 
29701  return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29702 }
29703 
29704 static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)
29705 {
29706  ma_result result;
29707 
29708  MA_ASSERT(pContext != NULL);
29709  MA_ASSERT(pIndex != NULL);
29710 
29711  if (pIndex != NULL) {
29712  *pIndex = (ma_uint32)-1;
29713  }
29714 
29715  if (deviceType == ma_device_type_playback) {
29716  ma_pa_sink_info sinkInfo;
29717  result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo);
29718  if (result != MA_SUCCESS) {
29719  return result;
29720  }
29721 
29722  if (pIndex != NULL) {
29723  *pIndex = sinkInfo.index;
29724  }
29725  }
29726 
29727  if (deviceType == ma_device_type_capture) {
29728  ma_pa_source_info sourceInfo;
29729  result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo);
29730  if (result != MA_SUCCESS) {
29731  return result;
29732  }
29733 
29734  if (pIndex != NULL) {
29735  *pIndex = sourceInfo.index;
29736  }
29737  }
29738 
29739  return MA_SUCCESS;
29740 }
29741 
29742 
29743 typedef struct
29744 {
29745  ma_context* pContext;
29746  ma_enum_devices_callback_proc callback;
29747  void* pUserData;
29748  ma_bool32 isTerminated;
29749  ma_uint32 defaultDeviceIndexPlayback;
29750  ma_uint32 defaultDeviceIndexCapture;
29751 } ma_context_enumerate_devices_callback_data__pulse;
29752 
29753 static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
29754 {
29755  ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
29756  ma_device_info deviceInfo;
29757 
29758  MA_ASSERT(pData != NULL);
29759 
29760  if (endOfList || pData->isTerminated) {
29761  return;
29762  }
29763 
29764  MA_ZERO_OBJECT(&deviceInfo);
29765 
29766  /* The name from PulseAudio is the ID for miniaudio. */
29767  if (pSinkInfo->name != NULL) {
29768  ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
29769  }
29770 
29771  /* The description from PulseAudio is the name for miniaudio. */
29772  if (pSinkInfo->description != NULL) {
29773  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
29774  }
29775 
29776  if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) {
29777  deviceInfo.isDefault = MA_TRUE;
29778  }
29779 
29780  pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
29781 
29782  (void)pPulseContext; /* Unused. */
29783 }
29784 
29785 static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData)
29786 {
29787  ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
29788  ma_device_info deviceInfo;
29789 
29790  MA_ASSERT(pData != NULL);
29791 
29792  if (endOfList || pData->isTerminated) {
29793  return;
29794  }
29795 
29796  MA_ZERO_OBJECT(&deviceInfo);
29797 
29798  /* The name from PulseAudio is the ID for miniaudio. */
29799  if (pSourceInfo->name != NULL) {
29800  ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1);
29801  }
29802 
29803  /* The description from PulseAudio is the name for miniaudio. */
29804  if (pSourceInfo->description != NULL) {
29805  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1);
29806  }
29807 
29808  if (pSourceInfo->index == pData->defaultDeviceIndexCapture) {
29809  deviceInfo.isDefault = MA_TRUE;
29810  }
29811 
29812  pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
29813 
29814  (void)pPulseContext; /* Unused. */
29815 }
29816 
29817 static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
29818 {
29819  ma_result result = MA_SUCCESS;
29820  ma_context_enumerate_devices_callback_data__pulse callbackData;
29821  ma_pa_operation* pOP = NULL;
29822 
29823  MA_ASSERT(pContext != NULL);
29824  MA_ASSERT(callback != NULL);
29825 
29826  callbackData.pContext = pContext;
29827  callbackData.callback = callback;
29828  callbackData.pUserData = pUserData;
29829  callbackData.isTerminated = MA_FALSE;
29830  callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1;
29831  callbackData.defaultDeviceIndexCapture = (ma_uint32)-1;
29832 
29833  /* We need to get the index of the default devices. */
29834  ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback);
29835  ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture);
29836 
29837  /* Playback. */
29838  if (!callbackData.isTerminated) {
29839  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);
29840  if (pOP == NULL) {
29841  result = MA_ERROR;
29842  goto done;
29843  }
29844 
29845  result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29846  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
29847 
29848  if (result != MA_SUCCESS) {
29849  goto done;
29850  }
29851  }
29852 
29853 
29854  /* Capture. */
29855  if (!callbackData.isTerminated) {
29856  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);
29857  if (pOP == NULL) {
29858  result = MA_ERROR;
29859  goto done;
29860  }
29861 
29862  result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29863  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
29864 
29865  if (result != MA_SUCCESS) {
29866  goto done;
29867  }
29868  }
29869 
29870 done:
29871  return result;
29872 }
29873 
29874 
29875 typedef struct
29876 {
29877  ma_device_info* pDeviceInfo;
29878  ma_uint32 defaultDeviceIndex;
29879  ma_bool32 foundDevice;
29880 } ma_context_get_device_info_callback_data__pulse;
29881 
29882 static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
29883 {
29884  ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
29885 
29886  if (endOfList > 0) {
29887  return;
29888  }
29889 
29890  MA_ASSERT(pData != NULL);
29891  pData->foundDevice = MA_TRUE;
29892 
29893  if (pInfo->name != NULL) {
29894  ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
29895  }
29896 
29897  if (pInfo->description != NULL) {
29898  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
29899  }
29900 
29901  /*
29902  We're just reporting a single data format here. I think technically PulseAudio might support
29903  all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
29904  report the "native" device format.
29905  */
29906  pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
29907  pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
29908  pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
29909  pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
29910  pData->pDeviceInfo->nativeDataFormatCount = 1;
29911 
29912  if (pData->defaultDeviceIndex == pInfo->index) {
29913  pData->pDeviceInfo->isDefault = MA_TRUE;
29914  }
29915 
29916  (void)pPulseContext; /* Unused. */
29917 }
29918 
29919 static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
29920 {
29921  ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
29922 
29923  if (endOfList > 0) {
29924  return;
29925  }
29926 
29927  MA_ASSERT(pData != NULL);
29928  pData->foundDevice = MA_TRUE;
29929 
29930  if (pInfo->name != NULL) {
29931  ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
29932  }
29933 
29934  if (pInfo->description != NULL) {
29935  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
29936  }
29937 
29938  /*
29939  We're just reporting a single data format here. I think technically PulseAudio might support
29940  all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
29941  report the "native" device format.
29942  */
29943  pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
29944  pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
29945  pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
29946  pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
29947  pData->pDeviceInfo->nativeDataFormatCount = 1;
29948 
29949  if (pData->defaultDeviceIndex == pInfo->index) {
29950  pData->pDeviceInfo->isDefault = MA_TRUE;
29951  }
29952 
29953  (void)pPulseContext; /* Unused. */
29954 }
29955 
29956 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)
29957 {
29958  ma_result result = MA_SUCCESS;
29959  ma_context_get_device_info_callback_data__pulse callbackData;
29960  ma_pa_operation* pOP = NULL;
29961  const char* pDeviceName = NULL;
29962 
29963  MA_ASSERT(pContext != NULL);
29964 
29965  callbackData.pDeviceInfo = pDeviceInfo;
29966  callbackData.foundDevice = MA_FALSE;
29967 
29968  if (pDeviceID != NULL) {
29969  pDeviceName = pDeviceID->pulse;
29970  } else {
29971  pDeviceName = NULL;
29972  }
29973 
29974  result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex);
29975 
29976  if (deviceType == ma_device_type_playback) {
29977  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);
29978  } else {
29979  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);
29980  }
29981 
29982  if (pOP != NULL) {
29983  ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29984  } else {
29985  result = MA_ERROR;
29986  goto done;
29987  }
29988 
29989  if (!callbackData.foundDevice) {
29990  result = MA_NO_DEVICE;
29991  goto done;
29992  }
29993 
29994 done:
29995  return result;
29996 }
29997 
29998 static ma_result ma_device_uninit__pulse(ma_device* pDevice)
29999 {
30000  ma_context* pContext;
30001 
30002  MA_ASSERT(pDevice != NULL);
30003 
30004  pContext = pDevice->pContext;
30005  MA_ASSERT(pContext != NULL);
30006 
30007  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30008  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30009  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30010  }
30011 
30012  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30013  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30014  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30015  }
30016 
30017  if (pDevice->type == ma_device_type_duplex) {
30018  ma_duplex_rb_uninit(&pDevice->duplexRB);
30019  }
30020 
30021  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
30022  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
30023  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
30024 
30025  return MA_SUCCESS;
30026 }
30027 
30028 static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
30029 {
30030  ma_pa_buffer_attr attr;
30031  attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
30032  attr.tlength = attr.maxlength / periods;
30033  attr.prebuf = (ma_uint32)-1;
30034  attr.minreq = (ma_uint32)-1;
30035  attr.fragsize = attr.maxlength / periods;
30036 
30037  return attr;
30038 }
30039 
30040 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)
30041 {
30042  static int g_StreamCounter = 0;
30043  char actualStreamName[256];
30044 
30045  if (pStreamName != NULL) {
30046  ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
30047  } else {
30048  ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
30049  ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
30050  }
30051  g_StreamCounter += 1;
30052 
30053  return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
30054 }
30055 
30056 
30057 static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
30058 {
30059  ma_device* pDevice = (ma_device*)pUserData;
30060  ma_uint32 bpf;
30061  ma_uint32 deviceState;
30062  ma_uint64 frameCount;
30063  ma_uint64 framesProcessed;
30064 
30065  MA_ASSERT(pDevice != NULL);
30066 
30067  /*
30068  Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
30069  can fire this callback before the stream has even started. Ridiculous.
30070  */
30071  deviceState = ma_device_get_state(pDevice);
30072  if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
30073  return;
30074  }
30075 
30076  bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
30077  MA_ASSERT(bpf > 0);
30078 
30079  frameCount = byteCount / bpf;
30080  framesProcessed = 0;
30081 
30082  while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) {
30083  const void* pMappedPCMFrames;
30084  size_t bytesMapped;
30085  ma_uint64 framesMapped;
30086 
30087  int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped);
30088  if (pulseResult < 0) {
30089  break; /* Failed to map. Abort. */
30090  }
30091 
30092  framesMapped = bytesMapped / bpf;
30093  if (framesMapped > 0) {
30094  if (pMappedPCMFrames != NULL) {
30095  ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped);
30096  } else {
30097  /* It's a hole. */
30098  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n");
30099  }
30100 
30101  pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream);
30102  if (pulseResult < 0) {
30103  break; /* Failed to drop the buffer. */
30104  }
30105 
30106  framesProcessed += framesMapped;
30107 
30108  } else {
30109  /* Nothing was mapped. Just abort. */
30110  break;
30111  }
30112  }
30113 }
30114 
30115 static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed)
30116 {
30117  ma_result result = MA_SUCCESS;
30118  ma_uint64 framesProcessed = 0;
30119  size_t bytesMapped;
30120  ma_uint32 bpf;
30121  ma_uint32 deviceState;
30122 
30123  MA_ASSERT(pDevice != NULL);
30124  MA_ASSERT(pStream != NULL);
30125 
30126  bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
30127  MA_ASSERT(bpf > 0);
30128 
30129  deviceState = ma_device_get_state(pDevice);
30130 
30131  bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream);
30132  if (bytesMapped != (size_t)-1) {
30133  if (bytesMapped > 0) {
30134  ma_uint64 framesMapped;
30135  void* pMappedPCMFrames;
30136  int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped);
30137  if (pulseResult < 0) {
30138  result = ma_result_from_pulse(pulseResult);
30139  goto done;
30140  }
30141 
30142  framesMapped = bytesMapped / bpf;
30143 
30144  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. */
30145  ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped);
30146  } else {
30147  /* Device is not started. Write silence. */
30148  ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels);
30149  }
30150 
30151  pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE);
30152  if (pulseResult < 0) {
30153  result = ma_result_from_pulse(pulseResult);
30154  goto done; /* Failed to write data to stream. */
30155  }
30156 
30157  framesProcessed += framesMapped;
30158  } else {
30159  result = MA_SUCCESS; /* No data available for writing. */
30160  goto done;
30161  }
30162  } else {
30163  result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */
30164  goto done;
30165  }
30166 
30167 done:
30168  if (pFramesProcessed != NULL) {
30169  *pFramesProcessed = framesProcessed;
30170  }
30171 
30172  return result;
30173 }
30174 
30175 static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
30176 {
30177  ma_device* pDevice = (ma_device*)pUserData;
30178  ma_uint32 bpf;
30179  ma_uint64 frameCount;
30180  ma_uint64 framesProcessed;
30181  ma_uint32 deviceState;
30182  ma_result result;
30183 
30184  MA_ASSERT(pDevice != NULL);
30185 
30186  /*
30187  Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
30188  can fire this callback before the stream has even started. Ridiculous.
30189  */
30190  deviceState = ma_device_get_state(pDevice);
30191  if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
30192  return;
30193  }
30194 
30195  bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
30196  MA_ASSERT(bpf > 0);
30197 
30198  frameCount = byteCount / bpf;
30199  framesProcessed = 0;
30200 
30201  while (framesProcessed < frameCount) {
30202  ma_uint64 framesProcessedThisIteration;
30203 
30204  /* Don't keep trying to process frames if the device isn't started. */
30205  deviceState = ma_device_get_state(pDevice);
30206  if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
30207  break;
30208  }
30209 
30210  result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration);
30211  if (result != MA_SUCCESS) {
30212  break;
30213  }
30214 
30215  framesProcessed += framesProcessedThisIteration;
30216  }
30217 }
30218 
30219 static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData)
30220 {
30221  ma_device* pDevice = (ma_device*)pUserData;
30222  int suspended;
30223 
30224  (void)pStream;
30225 
30226  suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream);
30227  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);
30228 
30229  if (suspended < 0) {
30230  return;
30231  }
30232 
30233  if (suspended == 1) {
30234  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n");
30235  ma_device__on_notification_stopped(pDevice);
30236  } else {
30237  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n");
30238  ma_device__on_notification_started(pDevice);
30239  }
30240 }
30241 
30242 static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData)
30243 {
30244  ma_device* pDevice = (ma_device*)pUserData;
30245 
30246  (void)pStream;
30247  (void)pUserData;
30248 
30249  ma_device__on_notification_rerouted(pDevice);
30250 }
30251 
30252 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
30253 {
30254  /*
30255  There have been reports from users where buffers of < ~20ms result glitches when running through
30256  PipeWire. To work around this we're going to have to use a different default buffer size.
30257  */
30258  const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25;
30259  const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;
30260 
30261  MA_ASSERT(nativeSampleRate != 0);
30262 
30263  if (pDescriptor->periodSizeInFrames == 0) {
30264  if (pDescriptor->periodSizeInMilliseconds == 0) {
30265  if (performanceProfile == ma_performance_profile_low_latency) {
30266  return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate);
30267  } else {
30268  return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate);
30269  }
30270  } else {
30271  return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
30272  }
30273  } else {
30274  return pDescriptor->periodSizeInFrames;
30275  }
30276 }
30277 
30278 static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
30279 {
30280  /*
30281  Notes for PulseAudio:
30282 
30283  - When both the period size in frames and milliseconds are 0, we default to miniaudio's
30284  default buffer sizes rather than leaving it up to PulseAudio because I don't trust
30285  PulseAudio to give us any kind of reasonable latency by default.
30286 
30287  - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this
30288  flag, capture mode will just not work properly until you open another PulseAudio app.
30289  */
30290 
30291  ma_result result = MA_SUCCESS;
30292  int error = 0;
30293  const char* devPlayback = NULL;
30294  const char* devCapture = NULL;
30295  ma_format format = ma_format_unknown;
30296  ma_uint32 channels = 0;
30297  ma_uint32 sampleRate = 0;
30298  ma_pa_sink_info sinkInfo;
30299  ma_pa_source_info sourceInfo;
30300  ma_pa_sample_spec ss;
30301  ma_pa_channel_map cmap;
30302  ma_pa_buffer_attr attr;
30303  const ma_pa_sample_spec* pActualSS = NULL;
30304  const ma_pa_buffer_attr* pActualAttr = NULL;
30305  ma_uint32 iChannel;
30306  ma_pa_stream_flags_t streamFlags;
30307 
30308  MA_ASSERT(pDevice != NULL);
30309  MA_ZERO_OBJECT(&pDevice->pulse);
30310 
30311  if (pConfig->deviceType == ma_device_type_loopback) {
30312  return MA_DEVICE_TYPE_NOT_SUPPORTED;
30313  }
30314 
30315  /* No exclusive mode with the PulseAudio backend. */
30316  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
30317  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
30318  return MA_SHARE_MODE_NOT_SUPPORTED;
30319  }
30320 
30321  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30322  if (pDescriptorPlayback->pDeviceID != NULL) {
30323  devPlayback = pDescriptorPlayback->pDeviceID->pulse;
30324  }
30325 
30326  format = pDescriptorPlayback->format;
30327  channels = pDescriptorPlayback->channels;
30328  sampleRate = pDescriptorPlayback->sampleRate;
30329  }
30330 
30331  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30332  if (pDescriptorCapture->pDeviceID != NULL) {
30333  devCapture = pDescriptorCapture->pDeviceID->pulse;
30334  }
30335 
30336  format = pDescriptorCapture->format;
30337  channels = pDescriptorCapture->channels;
30338  sampleRate = pDescriptorCapture->sampleRate;
30339  }
30340 
30341 
30342 
30343  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);
30344  if (result != MA_SUCCESS) {
30345  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n");
30346  return result;
30347  }
30348 
30349  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30350  result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
30351  if (result != MA_SUCCESS) {
30352  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.");
30353  goto on_error0;
30354  }
30355 
30356  ss = sourceInfo.sample_spec;
30357  cmap = sourceInfo.channel_map;
30358 
30359  /* Use the requested channel count if we have one. */
30360  if (pDescriptorCapture->channels != 0) {
30361  ss.channels = pDescriptorCapture->channels;
30362  }
30363 
30364  /* Use a default channel map. */
30365  ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT);
30366 
30367  /* Use the requested sample rate if one was specified. */
30368  if (pDescriptorCapture->sampleRate != 0) {
30369  ss.rate = pDescriptorCapture->sampleRate;
30370  }
30371  streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY;
30372 
30373  if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
30374  if (ma_is_little_endian()) {
30375  ss.format = MA_PA_SAMPLE_FLOAT32LE;
30376  } else {
30377  ss.format = MA_PA_SAMPLE_FLOAT32BE;
30378  }
30379  streamFlags |= MA_PA_STREAM_FIX_FORMAT;
30380  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");
30381  }
30382  if (ss.rate == 0) {
30383  ss.rate = MA_DEFAULT_SAMPLE_RATE;
30384  streamFlags |= MA_PA_STREAM_FIX_RATE;
30385  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
30386  }
30387  if (ss.channels == 0) {
30388  ss.channels = MA_DEFAULT_CHANNELS;
30389  streamFlags |= MA_PA_STREAM_FIX_CHANNELS;
30390  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
30391  }
30392 
30393  /* We now have enough information to calculate our actual period size in frames. */
30394  pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
30395 
30396  attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
30397  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);
30398 
30399  pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
30400  if (pDevice->pulse.pStreamCapture == NULL) {
30401  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n");
30402  result = MA_ERROR;
30403  goto on_error0;
30404  }
30405 
30406 
30407  /* The callback needs to be set before connecting the stream. */
30408  ((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);
30409 
30410  /* State callback for checking when the device has been corked. */
30411  ((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);
30412 
30413  /* Rerouting notification. */
30414  ((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);
30415 
30416 
30417  /* Connect after we've got all of our internal state set up. */
30418  if (devCapture != NULL) {
30419  streamFlags |= MA_PA_STREAM_DONT_MOVE;
30420  }
30421 
30422  error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
30423  if (error != MA_PA_OK) {
30424  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.");
30425  result = ma_result_from_pulse(error);
30426  goto on_error1;
30427  }
30428 
30429  result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture);
30430  if (result != MA_SUCCESS) {
30431  goto on_error2;
30432  }
30433 
30434 
30435  /* Internal format. */
30436  pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30437  if (pActualSS != NULL) {
30438  ss = *pActualSS;
30439  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);
30440  } else {
30441  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n");
30442  }
30443 
30444  pDescriptorCapture->format = ma_format_from_pulse(ss.format);
30445  pDescriptorCapture->channels = ss.channels;
30446  pDescriptorCapture->sampleRate = ss.rate;
30447 
30448  if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) {
30449  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);
30450  result = MA_ERROR;
30451  goto on_error4;
30452  }
30453 
30454  /* Internal channel map. */
30455 
30456  /*
30457  Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
30458  the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
30459  and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
30460  all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
30461  fixed sooner than later. I might remove this hack later.
30462  */
30463  if (pDescriptorCapture->channels > 2) {
30464  for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) {
30465  pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
30466  }
30467  } else {
30468  /* Hack for mono and stereo. */
30469  if (pDescriptorCapture->channels == 1) {
30470  pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO;
30471  } else if (pDescriptorCapture->channels == 2) {
30472  pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
30473  pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
30474  } else {
30475  MA_ASSERT(MA_FALSE); /* Should never hit this. */
30476  }
30477  }
30478 
30479 
30480  /* Buffer. */
30481  pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30482  if (pActualAttr != NULL) {
30483  attr = *pActualAttr;
30484  }
30485 
30486  if (attr.fragsize > 0) {
30487  pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1);
30488  } else {
30489  pDescriptorCapture->periodCount = 1;
30490  }
30491 
30492  pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;
30493  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);
30494  }
30495 
30496  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30497  result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo);
30498  if (result != MA_SUCCESS) {
30499  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n");
30500  goto on_error2;
30501  }
30502 
30503  ss = sinkInfo.sample_spec;
30504  cmap = sinkInfo.channel_map;
30505 
30506  /* Use the requested channel count if we have one. */
30507  if (pDescriptorPlayback->channels != 0) {
30508  ss.channels = pDescriptorPlayback->channels;
30509  }
30510 
30511  /* Use a default channel map. */
30512  ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT);
30513 
30514 
30515  /* Use the requested sample rate if one was specified. */
30516  if (pDescriptorPlayback->sampleRate != 0) {
30517  ss.rate = pDescriptorPlayback->sampleRate;
30518  }
30519 
30520  streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY;
30521  if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
30522  if (ma_is_little_endian()) {
30523  ss.format = MA_PA_SAMPLE_FLOAT32LE;
30524  } else {
30525  ss.format = MA_PA_SAMPLE_FLOAT32BE;
30526  }
30527  streamFlags |= MA_PA_STREAM_FIX_FORMAT;
30528  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");
30529  }
30530  if (ss.rate == 0) {
30531  ss.rate = MA_DEFAULT_SAMPLE_RATE;
30532  streamFlags |= MA_PA_STREAM_FIX_RATE;
30533  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
30534  }
30535  if (ss.channels == 0) {
30536  ss.channels = MA_DEFAULT_CHANNELS;
30537  streamFlags |= MA_PA_STREAM_FIX_CHANNELS;
30538  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
30539  }
30540 
30541  /* We now have enough information to calculate the actual buffer size in frames. */
30542  pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
30543 
30544  attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);
30545 
30546  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);
30547 
30548  pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
30549  if (pDevice->pulse.pStreamPlayback == NULL) {
30550  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n");
30551  result = MA_ERROR;
30552  goto on_error2;
30553  }
30554 
30555 
30556  /*
30557  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
30558  device state of ma_device_state_uninitialized.
30559  */
30560  ((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);
30561 
30562  /* State callback for checking when the device has been corked. */
30563  ((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);
30564 
30565  /* Rerouting notification. */
30566  ((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);
30567 
30568 
30569  /* Connect after we've got all of our internal state set up. */
30570  if (devPlayback != NULL) {
30571  streamFlags |= MA_PA_STREAM_DONT_MOVE;
30572  }
30573 
30574  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);
30575  if (error != MA_PA_OK) {
30576  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.");
30577  result = ma_result_from_pulse(error);
30578  goto on_error3;
30579  }
30580 
30581  result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30582  if (result != MA_SUCCESS) {
30583  goto on_error3;
30584  }
30585 
30586 
30587  /* Internal format. */
30588  pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30589  if (pActualSS != NULL) {
30590  ss = *pActualSS;
30591  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);
30592  } else {
30593  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n");
30594  }
30595 
30596  pDescriptorPlayback->format = ma_format_from_pulse(ss.format);
30597  pDescriptorPlayback->channels = ss.channels;
30598  pDescriptorPlayback->sampleRate = ss.rate;
30599 
30600  if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) {
30601  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);
30602  result = MA_ERROR;
30603  goto on_error4;
30604  }
30605 
30606  /* Internal channel map. */
30607 
30608  /*
30609  Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
30610  the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
30611  and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
30612  all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
30613  fixed sooner than later. I might remove this hack later.
30614  */
30615  if (pDescriptorPlayback->channels > 2) {
30616  for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) {
30617  pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
30618  }
30619  } else {
30620  /* Hack for mono and stereo. */
30621  if (pDescriptorPlayback->channels == 1) {
30622  pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO;
30623  } else if (pDescriptorPlayback->channels == 2) {
30624  pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
30625  pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
30626  } else {
30627  MA_ASSERT(MA_FALSE); /* Should never hit this. */
30628  }
30629  }
30630 
30631 
30632  /* Buffer. */
30633  pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30634  if (pActualAttr != NULL) {
30635  attr = *pActualAttr;
30636  }
30637 
30638  if (attr.tlength > 0) {
30639  pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1);
30640  } else {
30641  pDescriptorPlayback->periodCount = 1;
30642  }
30643 
30644  pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount;
30645  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);
30646  }
30647 
30648 
30649  /*
30650  We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main
30651  part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for
30652  us later on because that will only do it if it's a fully asynchronous backend - i.e. the
30653  onDeviceDataLoop callback is NULL, which is not the case for PulseAudio.
30654  */
30655  if (pConfig->deviceType == ma_device_type_duplex) {
30656  ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format;
30657  ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels;
30658  ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate;
30659 
30660  result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
30661  if (result != MA_SUCCESS) {
30662  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result));
30663  goto on_error4;
30664  }
30665  }
30666 
30667  return MA_SUCCESS;
30668 
30669 
30670 on_error4:
30671  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30672  ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30673  }
30674 on_error3:
30675  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30676  ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30677  }
30678 on_error2:
30679  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30680  ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30681  }
30682 on_error1:
30683  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30684  ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30685  }
30686 on_error0:
30687  return result;
30688 }
30689 
30690 
30691 static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
30692 {
30693  ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
30694  MA_ASSERT(pIsSuccessful != NULL);
30695 
30696  *pIsSuccessful = (ma_bool32)success;
30697 
30698  (void)pStream; /* Unused. */
30699 }
30700 
30701 static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
30702 {
30703  ma_context* pContext = pDevice->pContext;
30704  ma_bool32 wasSuccessful;
30705  ma_pa_stream* pStream;
30706  ma_pa_operation* pOP;
30707  ma_result result;
30708 
30709  /* This should not be called with a duplex device type. */
30710  if (deviceType == ma_device_type_duplex) {
30711  return MA_INVALID_ARGS;
30712  }
30713 
30714  wasSuccessful = MA_FALSE;
30715 
30716  pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
30717  MA_ASSERT(pStream != NULL);
30718 
30719  pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
30720  if (pOP == NULL) {
30721  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.");
30722  return MA_ERROR;
30723  }
30724 
30725  result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
30726  if (result != MA_SUCCESS) {
30727  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.");
30728  return result;
30729  }
30730 
30731  if (!wasSuccessful) {
30732  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start");
30733  return MA_ERROR;
30734  }
30735 
30736  return MA_SUCCESS;
30737 }
30738 
30739 static ma_result ma_device_start__pulse(ma_device* pDevice)
30740 {
30741  ma_result result;
30742 
30743  MA_ASSERT(pDevice != NULL);
30744 
30745  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30746  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
30747  if (result != MA_SUCCESS) {
30748  return result;
30749  }
30750  }
30751 
30752  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30753  /*
30754  We need to fill some data before uncorking. Not doing this will result in the write callback
30755  never getting fired. We're not going to abort if writing fails because I still want the device
30756  to get uncorked.
30757  */
30758  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.*/
30759 
30760  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
30761  if (result != MA_SUCCESS) {
30762  return result;
30763  }
30764  }
30765 
30766  return MA_SUCCESS;
30767 }
30768 
30769 static ma_result ma_device_stop__pulse(ma_device* pDevice)
30770 {
30771  ma_result result;
30772 
30773  MA_ASSERT(pDevice != NULL);
30774 
30775  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30776  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
30777  if (result != MA_SUCCESS) {
30778  return result;
30779  }
30780  }
30781 
30782  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30783  /*
30784  Ideally we would drain the device here, but there's been cases where PulseAudio seems to be
30785  broken on some systems to the point where no audio processing seems to happen. When this
30786  happens, draining never completes and we get stuck here. For now I'm disabling draining of
30787  the device so we don't just freeze the application.
30788  */
30789  #if 0
30790  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);
30791  ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
30792  #endif
30793 
30794  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
30795  if (result != MA_SUCCESS) {
30796  return result;
30797  }
30798  }
30799 
30800  return MA_SUCCESS;
30801 }
30802 
30803 static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
30804 {
30805  int resultPA;
30806 
30807  MA_ASSERT(pDevice != NULL);
30808 
30809  /* NOTE: Don't start the device here. It'll be done at a higher level. */
30810 
30811  /*
30812  All data is handled through callbacks. All we need to do is iterate over the main loop and let
30813  the callbacks deal with it.
30814  */
30815  while (ma_device_get_state(pDevice) == ma_device_state_started) {
30816  resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
30817  if (resultPA < 0) {
30818  break;
30819  }
30820  }
30821 
30822  /* NOTE: Don't stop the device here. It'll be done at a higher level. */
30823  return MA_SUCCESS;
30824 }
30825 
30826 static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
30827 {
30828  MA_ASSERT(pDevice != NULL);
30829 
30830  ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
30831 
30832  return MA_SUCCESS;
30833 }
30834 
30835 static ma_result ma_context_uninit__pulse(ma_context* pContext)
30836 {
30837  MA_ASSERT(pContext != NULL);
30838  MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
30839 
30840  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);
30841  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);
30842  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
30843 
30844  ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
30845  ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
30846 
30847 #ifndef MA_NO_RUNTIME_LINKING
30848  ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO);
30849 #endif
30850 
30851  return MA_SUCCESS;
30852 }
30853 
30854 static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
30855 {
30856  ma_result result;
30857 #ifndef MA_NO_RUNTIME_LINKING
30858  const char* libpulseNames[] = {
30859  "libpulse.so",
30860  "libpulse.so.0"
30861  };
30862  size_t i;
30863 
30864  for (i = 0; i < ma_countof(libpulseNames); ++i) {
30865  pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]);
30866  if (pContext->pulse.pulseSO != NULL) {
30867  break;
30868  }
30869  }
30870 
30871  if (pContext->pulse.pulseSO == NULL) {
30872  return MA_NO_BACKEND;
30873  }
30874 
30875  pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new");
30876  pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free");
30877  pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit");
30878  pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api");
30879  pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate");
30880  pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup");
30881  pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new");
30882  pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free");
30883  pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start");
30884  pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop");
30885  pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock");
30886  pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock");
30887  pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait");
30888  pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal");
30889  pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept");
30890  pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval");
30891  pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api");
30892  pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread");
30893  pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name");
30894  pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new");
30895  pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref");
30896  pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect");
30897  pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect");
30898  pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback");
30899  pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state");
30900  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");
30901  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");
30902  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");
30903  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");
30904  pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref");
30905  pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state");
30906  pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend");
30907  pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid");
30908  pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible");
30909  pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new");
30910  pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref");
30911  pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback");
30912  pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record");
30913  pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect");
30914  pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state");
30915  pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
30916  pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map");
30917  pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
30918  pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
30919  pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name");
30920  pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback");
30921  pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback");
30922  pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback");
30923  pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback");
30924  pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended");
30925  pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush");
30926  pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain");
30927  pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked");
30928  pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork");
30929  pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger");
30930  pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write");
30931  pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write");
30932  pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek");
30933  pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop");
30934  pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size");
30935  pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size");
30936 #else
30937  /* This strange assignment system is just for type safety. */
30938  ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
30939  ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
30940  ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit;
30941  ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
30942  ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
30943  ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
30944  ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new;
30945  ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free;
30946  ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start;
30947  ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop;
30948  ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock;
30949  ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock;
30950  ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait;
30951  ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal;
30952  ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept;
30953  ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval;
30954  ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api;
30955  ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread;
30956  ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
30957  ma_pa_context_new_proc _pa_context_new = pa_context_new;
30958  ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
30959  ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
30960  ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
30961  ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
30962  ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
30963  ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
30964  ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
30965  ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
30966  ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
30967  ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
30968  ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
30969  ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
30970  ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
30971  ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
30972  ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
30973  ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
30974  ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
30975  ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
30976  ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
30977  ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
30978  ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
30979  ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
30980  ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
30981  ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
30982  ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
30983  ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
30984  ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
30985  ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback;
30986  ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback;
30987  ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended;
30988  ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
30989  ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
30990  ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
30991  ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
30992  ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
30993  ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
30994  ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
30995  ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
30996  ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
30997  ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
30998  ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
30999 
31000  pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
31001  pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
31002  pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit;
31003  pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
31004  pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
31005  pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
31006  pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new;
31007  pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free;
31008  pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start;
31009  pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop;
31010  pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock;
31011  pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock;
31012  pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait;
31013  pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal;
31014  pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept;
31015  pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval;
31016  pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api;
31017  pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread;
31018  pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name;
31019  pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
31020  pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
31021  pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
31022  pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
31023  pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
31024  pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
31025  pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
31026  pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
31027  pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
31028  pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
31029  pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
31030  pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
31031  pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
31032  pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
31033  pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
31034  pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
31035  pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
31036  pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
31037  pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
31038  pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
31039  pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
31040  pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
31041  pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
31042  pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
31043  pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
31044  pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
31045  pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
31046  pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
31047  pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback;
31048  pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback;
31049  pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended;
31050  pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
31051  pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
31052  pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
31053  pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
31054  pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
31055  pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
31056  pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
31057  pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
31058  pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
31059  pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
31060  pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
31061 #endif
31062 
31063  /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */
31064  pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
31065  if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {
31066  return MA_OUT_OF_MEMORY;
31067  }
31068 
31069  pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
31070  if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {
31071  ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
31072  return MA_OUT_OF_MEMORY;
31073  }
31074 
31075  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);
31076  if (result != MA_SUCCESS) {
31077  ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
31078  ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
31079  #ifndef MA_NO_RUNTIME_LINKING
31080  ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO);
31081  #endif
31082  return result;
31083  }
31084 
31085  /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */
31086  pCallbacks->onContextInit = ma_context_init__pulse;
31087  pCallbacks->onContextUninit = ma_context_uninit__pulse;
31088  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse;
31089  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse;
31090  pCallbacks->onDeviceInit = ma_device_init__pulse;
31091  pCallbacks->onDeviceUninit = ma_device_uninit__pulse;
31092  pCallbacks->onDeviceStart = ma_device_start__pulse;
31093  pCallbacks->onDeviceStop = ma_device_stop__pulse;
31094  pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */
31095  pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */
31096  pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse;
31097  pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse;
31098 
31099  return MA_SUCCESS;
31100 }
31101 #endif
31102 
31103 
31104 /******************************************************************************
31105 
31106 JACK Backend
31107 
31108 ******************************************************************************/
31109 #ifdef MA_HAS_JACK
31110 
31111 /* It is assumed jack.h is available when compile-time linking is being used. */
31112 #ifdef MA_NO_RUNTIME_LINKING
31113 #include <jack/jack.h>
31114 
31115 typedef jack_nframes_t ma_jack_nframes_t;
31116 typedef jack_options_t ma_jack_options_t;
31117 typedef jack_status_t ma_jack_status_t;
31118 typedef jack_client_t ma_jack_client_t;
31119 typedef jack_port_t ma_jack_port_t;
31120 typedef JackProcessCallback ma_JackProcessCallback;
31121 typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
31122 typedef JackShutdownCallback ma_JackShutdownCallback;
31123 #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
31124 #define ma_JackNoStartServer JackNoStartServer
31125 #define ma_JackPortIsInput JackPortIsInput
31126 #define ma_JackPortIsOutput JackPortIsOutput
31127 #define ma_JackPortIsPhysical JackPortIsPhysical
31128 #else
31129 typedef ma_uint32 ma_jack_nframes_t;
31130 typedef int ma_jack_options_t;
31131 typedef int ma_jack_status_t;
31132 typedef struct ma_jack_client_t ma_jack_client_t;
31133 typedef struct ma_jack_port_t ma_jack_port_t;
31134 typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
31135 typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
31136 typedef void (* ma_JackShutdownCallback) (void* arg);
31137 #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
31138 #define ma_JackNoStartServer 1
31139 #define ma_JackPortIsInput 1
31140 #define ma_JackPortIsOutput 2
31141 #define ma_JackPortIsPhysical 4
31142 #endif
31143 
31144 typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
31145 typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
31146 typedef int (* ma_jack_client_name_size_proc) (void);
31147 typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
31148 typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
31149 typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
31150 typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
31151 typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
31152 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);
31153 typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
31154 typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
31155 typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
31156 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);
31157 typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
31158 typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
31159 typedef void (* ma_jack_free_proc) (void* ptr);
31160 
31161 static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
31162 {
31163  size_t maxClientNameSize;
31164  char clientName[256];
31165  ma_jack_status_t status;
31166  ma_jack_client_t* pClient;
31167 
31168  MA_ASSERT(pContext != NULL);
31169  MA_ASSERT(ppClient != NULL);
31170 
31171  if (ppClient) {
31172  *ppClient = NULL;
31173  }
31174 
31175  maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
31176  ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
31177 
31178  pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
31179  if (pClient == NULL) {
31180  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31181  }
31182 
31183  if (ppClient) {
31184  *ppClient = pClient;
31185  }
31186 
31187  return MA_SUCCESS;
31188 }
31189 
31190 
31191 static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
31192 {
31193  ma_bool32 cbResult = MA_TRUE;
31194 
31195  MA_ASSERT(pContext != NULL);
31196  MA_ASSERT(callback != NULL);
31197 
31198  /* Playback. */
31199  if (cbResult) {
31200  ma_device_info deviceInfo;
31201  MA_ZERO_OBJECT(&deviceInfo);
31202  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31203  deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
31204  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
31205  }
31206 
31207  /* Capture. */
31208  if (cbResult) {
31209  ma_device_info deviceInfo;
31210  MA_ZERO_OBJECT(&deviceInfo);
31211  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31212  deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
31213  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
31214  }
31215 
31216  (void)cbResult; /* For silencing a static analysis warning. */
31217 
31218  return MA_SUCCESS;
31219 }
31220 
31221 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)
31222 {
31223  ma_jack_client_t* pClient;
31224  ma_result result;
31225  const char** ppPorts;
31226 
31227  MA_ASSERT(pContext != NULL);
31228 
31229  if (pDeviceID != NULL && pDeviceID->jack != 0) {
31230  return MA_NO_DEVICE; /* Don't know the device. */
31231  }
31232 
31233  /* Name / Description */
31234  if (deviceType == ma_device_type_playback) {
31235  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31236  } else {
31237  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31238  }
31239 
31240  /* Jack only uses default devices. */
31241  pDeviceInfo->isDefault = MA_TRUE;
31242 
31243  /* Jack only supports f32 and has a specific channel count and sample rate. */
31244  pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
31245 
31246  /* The channel count and sample rate can only be determined by opening the device. */
31247  result = ma_context_open_client__jack(pContext, &pClient);
31248  if (result != MA_SUCCESS) {
31249  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
31250  return result;
31251  }
31252 
31253  pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
31254  pDeviceInfo->nativeDataFormats[0].channels = 0;
31255 
31256  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));
31257  if (ppPorts == NULL) {
31258  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
31259  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
31260  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31261  }
31262 
31263  while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) {
31264  pDeviceInfo->nativeDataFormats[0].channels += 1;
31265  }
31266 
31267  pDeviceInfo->nativeDataFormats[0].flags = 0;
31268  pDeviceInfo->nativeDataFormatCount = 1;
31269 
31270  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
31271  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
31272 
31273  (void)pContext;
31274  return MA_SUCCESS;
31275 }
31276 
31277 
31278 static ma_result ma_device_uninit__jack(ma_device* pDevice)
31279 {
31280  ma_context* pContext;
31281 
31282  MA_ASSERT(pDevice != NULL);
31283 
31284  pContext = pDevice->pContext;
31285  MA_ASSERT(pContext != NULL);
31286 
31287  if (pDevice->jack.pClient != NULL) {
31288  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
31289  }
31290 
31291  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31292  ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
31293  ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
31294  }
31295 
31296  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31297  ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
31298  ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks);
31299  }
31300 
31301  return MA_SUCCESS;
31302 }
31303 
31304 static void ma_device__jack_shutdown_callback(void* pUserData)
31305 {
31306  /* JACK died. Stop the device. */
31307  ma_device* pDevice = (ma_device*)pUserData;
31308  MA_ASSERT(pDevice != NULL);
31309 
31310  ma_device_stop(pDevice);
31311 }
31312 
31313 static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
31314 {
31315  ma_device* pDevice = (ma_device*)pUserData;
31316  MA_ASSERT(pDevice != NULL);
31317 
31318  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31319  size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
31320  float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
31321  if (pNewBuffer == NULL) {
31322  return MA_OUT_OF_MEMORY;
31323  }
31324 
31325  ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
31326 
31327  pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
31328  pDevice->playback.internalPeriodSizeInFrames = frameCount;
31329  }
31330 
31331  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31332  size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
31333  float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
31334  if (pNewBuffer == NULL) {
31335  return MA_OUT_OF_MEMORY;
31336  }
31337 
31338  ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
31339 
31340  pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
31341  pDevice->playback.internalPeriodSizeInFrames = frameCount;
31342  }
31343 
31344  return 0;
31345 }
31346 
31347 static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
31348 {
31349  ma_device* pDevice;
31350  ma_context* pContext;
31351  ma_uint32 iChannel;
31352 
31353  pDevice = (ma_device*)pUserData;
31354  MA_ASSERT(pDevice != NULL);
31355 
31356  pContext = pDevice->pContext;
31357  MA_ASSERT(pContext != NULL);
31358 
31359  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31360  /* Channels need to be interleaved. */
31361  for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
31362  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);
31363  if (pSrc != NULL) {
31364  float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
31365  ma_jack_nframes_t iFrame;
31366  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31367  *pDst = *pSrc;
31368 
31369  pDst += pDevice->capture.internalChannels;
31370  pSrc += 1;
31371  }
31372  }
31373  }
31374 
31375  ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount);
31376  }
31377 
31378  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31379  ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount);
31380 
31381  /* Channels need to be deinterleaved. */
31382  for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
31383  float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount);
31384  if (pDst != NULL) {
31385  const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
31386  ma_jack_nframes_t iFrame;
31387  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31388  *pDst = *pSrc;
31389 
31390  pDst += 1;
31391  pSrc += pDevice->playback.internalChannels;
31392  }
31393  }
31394  }
31395  }
31396 
31397  return 0;
31398 }
31399 
31400 static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
31401 {
31402  ma_result result;
31403  ma_uint32 periodSizeInFrames;
31404 
31405  MA_ASSERT(pConfig != NULL);
31406  MA_ASSERT(pDevice != NULL);
31407 
31408  if (pConfig->deviceType == ma_device_type_loopback) {
31409  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported.");
31410  return MA_DEVICE_TYPE_NOT_SUPPORTED;
31411  }
31412 
31413  /* Only supporting default devices with JACK. */
31414  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) ||
31415  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) {
31416  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported.");
31417  return MA_NO_DEVICE;
31418  }
31419 
31420  /* No exclusive mode with the JACK backend. */
31421  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
31422  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
31423  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported.");
31424  return MA_SHARE_MODE_NOT_SUPPORTED;
31425  }
31426 
31427  /* Open the client. */
31428  result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
31429  if (result != MA_SUCCESS) {
31430  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
31431  return result;
31432  }
31433 
31434  /* Callbacks. */
31435  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) {
31436  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.");
31437  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31438  }
31439  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) {
31440  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.");
31441  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31442  }
31443 
31444  ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
31445 
31446 
31447  /* The buffer size in frames can change. */
31448  periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
31449 
31450  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
31451  ma_uint32 iPort;
31452  const char** ppPorts;
31453 
31454  pDescriptorCapture->format = ma_format_f32;
31455  pDescriptorCapture->channels = 0;
31456  pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
31457  ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
31458 
31459  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);
31460  if (ppPorts == NULL) {
31461  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
31462  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31463  }
31464 
31465  /* Need to count the number of ports first so we can allocate some memory. */
31466  while (ppPorts[pDescriptorCapture->channels] != NULL) {
31467  pDescriptorCapture->channels += 1;
31468  }
31469 
31470  pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks);
31471  if (pDevice->jack.ppPortsCapture == NULL) {
31472  return MA_OUT_OF_MEMORY;
31473  }
31474 
31475  for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) {
31476  char name[64];
31477  ma_strcpy_s(name, sizeof(name), "capture");
31478  ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
31479 
31480  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);
31481  if (pDevice->jack.ppPortsCapture[iPort] == NULL) {
31482  ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
31483  ma_device_uninit__jack(pDevice);
31484  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
31485  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31486  }
31487  }
31488 
31489  ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
31490 
31491  pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
31492  pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
31493 
31494  pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks);
31495  if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
31496  ma_device_uninit__jack(pDevice);
31497  return MA_OUT_OF_MEMORY;
31498  }
31499  }
31500 
31501  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
31502  ma_uint32 iPort;
31503  const char** ppPorts;
31504 
31505  pDescriptorPlayback->format = ma_format_f32;
31506  pDescriptorPlayback->channels = 0;
31507  pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
31508  ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
31509 
31510  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);
31511  if (ppPorts == NULL) {
31512  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
31513  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31514  }
31515 
31516  /* Need to count the number of ports first so we can allocate some memory. */
31517  while (ppPorts[pDescriptorPlayback->channels] != NULL) {
31518  pDescriptorPlayback->channels += 1;
31519  }
31520 
31521  pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks);
31522  if (pDevice->jack.ppPortsPlayback == NULL) {
31523  ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
31524  return MA_OUT_OF_MEMORY;
31525  }
31526 
31527  for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) {
31528  char name[64];
31529  ma_strcpy_s(name, sizeof(name), "playback");
31530  ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
31531 
31532  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);
31533  if (pDevice->jack.ppPortsPlayback[iPort] == NULL) {
31534  ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
31535  ma_device_uninit__jack(pDevice);
31536  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
31537  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31538  }
31539  }
31540 
31541  ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
31542 
31543  pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
31544  pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
31545 
31546  pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks);
31547  if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
31548  ma_device_uninit__jack(pDevice);
31549  return MA_OUT_OF_MEMORY;
31550  }
31551  }
31552 
31553  return MA_SUCCESS;
31554 }
31555 
31556 
31557 static ma_result ma_device_start__jack(ma_device* pDevice)
31558 {
31559  ma_context* pContext = pDevice->pContext;
31560  int resultJACK;
31561  size_t i;
31562 
31563  resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
31564  if (resultJACK != 0) {
31565  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.");
31566  return MA_FAILED_TO_START_BACKEND_DEVICE;
31567  }
31568 
31569  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31570  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);
31571  if (ppServerPorts == NULL) {
31572  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
31573  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
31574  return MA_ERROR;
31575  }
31576 
31577  for (i = 0; ppServerPorts[i] != NULL; ++i) {
31578  const char* pServerPort = ppServerPorts[i];
31579  const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]);
31580 
31581  resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
31582  if (resultJACK != 0) {
31583  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
31584  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
31585  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
31586  return MA_ERROR;
31587  }
31588  }
31589 
31590  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
31591  }
31592 
31593  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31594  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);
31595  if (ppServerPorts == NULL) {
31596  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
31597  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
31598  return MA_ERROR;
31599  }
31600 
31601  for (i = 0; ppServerPorts[i] != NULL; ++i) {
31602  const char* pServerPort = ppServerPorts[i];
31603  const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]);
31604 
31605  resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
31606  if (resultJACK != 0) {
31607  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
31608  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
31609  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
31610  return MA_ERROR;
31611  }
31612  }
31613 
31614  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
31615  }
31616 
31617  return MA_SUCCESS;
31618 }
31619 
31620 static ma_result ma_device_stop__jack(ma_device* pDevice)
31621 {
31622  ma_context* pContext = pDevice->pContext;
31623 
31624  if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
31625  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.");
31626  return MA_ERROR;
31627  }
31628 
31629  ma_device__on_notification_stopped(pDevice);
31630 
31631  return MA_SUCCESS;
31632 }
31633 
31634 
31635 static ma_result ma_context_uninit__jack(ma_context* pContext)
31636 {
31637  MA_ASSERT(pContext != NULL);
31638  MA_ASSERT(pContext->backend == ma_backend_jack);
31639 
31640  ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
31641  pContext->jack.pClientName = NULL;
31642 
31643 #ifndef MA_NO_RUNTIME_LINKING
31644  ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO);
31645 #endif
31646 
31647  return MA_SUCCESS;
31648 }
31649 
31650 static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
31651 {
31652 #ifndef MA_NO_RUNTIME_LINKING
31653  const char* libjackNames[] = {
31654 #if defined(MA_WIN32)
31655  "libjack.dll",
31656  "libjack64.dll"
31657 #endif
31658 #if defined(MA_UNIX)
31659  "libjack.so",
31660  "libjack.so.0"
31661 #endif
31662  };
31663  size_t i;
31664 
31665  for (i = 0; i < ma_countof(libjackNames); ++i) {
31666  pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]);
31667  if (pContext->jack.jackSO != NULL) {
31668  break;
31669  }
31670  }
31671 
31672  if (pContext->jack.jackSO == NULL) {
31673  return MA_NO_BACKEND;
31674  }
31675 
31676  pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open");
31677  pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close");
31678  pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size");
31679  pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback");
31680  pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback");
31681  pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown");
31682  pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate");
31683  pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size");
31684  pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports");
31685  pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate");
31686  pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate");
31687  pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect");
31688  pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register");
31689  pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name");
31690  pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer");
31691  pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free");
31692 #else
31693  /*
31694  This strange assignment system is here just to ensure type safety of miniaudio's function pointer
31695  types. If anything differs slightly the compiler should throw a warning.
31696  */
31697  ma_jack_client_open_proc _jack_client_open = jack_client_open;
31698  ma_jack_client_close_proc _jack_client_close = jack_client_close;
31699  ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
31700  ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
31701  ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
31702  ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
31703  ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
31704  ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
31705  ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
31706  ma_jack_activate_proc _jack_activate = jack_activate;
31707  ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
31708  ma_jack_connect_proc _jack_connect = jack_connect;
31709  ma_jack_port_register_proc _jack_port_register = jack_port_register;
31710  ma_jack_port_name_proc _jack_port_name = jack_port_name;
31711  ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
31712  ma_jack_free_proc _jack_free = jack_free;
31713 
31714  pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
31715  pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
31716  pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
31717  pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
31718  pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
31719  pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
31720  pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
31721  pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
31722  pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
31723  pContext->jack.jack_activate = (ma_proc)_jack_activate;
31724  pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
31725  pContext->jack.jack_connect = (ma_proc)_jack_connect;
31726  pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
31727  pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
31728  pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
31729  pContext->jack.jack_free = (ma_proc)_jack_free;
31730 #endif
31731 
31732  if (pConfig->jack.pClientName != NULL) {
31733  pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
31734  }
31735  pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
31736 
31737  /*
31738  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
31739  a temporary client.
31740  */
31741  {
31742  ma_jack_client_t* pDummyClient;
31743  ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
31744  if (result != MA_SUCCESS) {
31745  ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
31746  #ifndef MA_NO_RUNTIME_LINKING
31747  ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO);
31748  #endif
31749  return MA_NO_BACKEND;
31750  }
31751 
31752  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
31753  }
31754 
31755 
31756  pCallbacks->onContextInit = ma_context_init__jack;
31757  pCallbacks->onContextUninit = ma_context_uninit__jack;
31758  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack;
31759  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack;
31760  pCallbacks->onDeviceInit = ma_device_init__jack;
31761  pCallbacks->onDeviceUninit = ma_device_uninit__jack;
31762  pCallbacks->onDeviceStart = ma_device_start__jack;
31763  pCallbacks->onDeviceStop = ma_device_stop__jack;
31764  pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */
31765  pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */
31766  pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */
31767 
31768  return MA_SUCCESS;
31769 }
31770 #endif /* JACK */
31771 
31772 
31773 
31774 /******************************************************************************
31775 
31776 Core Audio Backend
31777 
31778 References
31779 ==========
31780 - Technical Note TN2091: Device input using the HAL Output Audio Unit
31781  https://developer.apple.com/library/archive/technotes/tn2091/_index.html
31782 
31783 ******************************************************************************/
31784 #ifdef MA_HAS_COREAUDIO
31785 #include <TargetConditionals.h>
31786 
31787 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
31788  #define MA_APPLE_MOBILE
31789  #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
31790  #define MA_APPLE_TV
31791  #endif
31792  #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
31793  #define MA_APPLE_WATCH
31794  #endif
31795  #if __has_feature(objc_arc)
31796  #define MA_BRIDGE_TRANSFER __bridge_transfer
31797  #define MA_BRIDGE_RETAINED __bridge_retained
31798  #else
31799  #define MA_BRIDGE_TRANSFER
31800  #define MA_BRIDGE_RETAINED
31801  #endif
31802 #else
31803  #define MA_APPLE_DESKTOP
31804 #endif
31805 
31806 #if defined(MA_APPLE_DESKTOP)
31807 #include <CoreAudio/CoreAudio.h>
31808 #else
31809 #include <AVFoundation/AVFoundation.h>
31810 #endif
31811 
31812 #include <AudioToolbox/AudioToolbox.h>
31813 
31814 /* CoreFoundation */
31815 typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
31816 typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
31817 
31818 /* CoreAudio */
31819 #if defined(MA_APPLE_DESKTOP)
31820 typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
31821 typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
31822 typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
31823 typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
31824 typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
31825 #endif
31826 
31827 /* AudioToolbox */
31828 typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
31829 typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
31830 typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
31831 typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
31832 typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
31833 typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
31834 typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
31835 typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
31836 typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
31837 typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
31838 typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
31839 
31840 
31841 #define MA_COREAUDIO_OUTPUT_BUS 0
31842 #define MA_COREAUDIO_INPUT_BUS 1
31843 
31844 #if defined(MA_APPLE_DESKTOP)
31845 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
31846 #endif
31847 
31848 /*
31849 Core Audio
31850 
31851 So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
31852 apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
31853 needing to figure out how this darn thing works, I'm going to outline a few things here.
31854 
31855 Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
31856 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
31857 that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
31858 and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
31859 distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
31860 
31861 Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
31862 retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
31863 data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
31864 devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
31865 the central APIs for retrieving information about the system and specific devices.
31866 
31867 To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
31868 structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
31869 which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
31870 typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
31871 kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
31872 kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different.
31873 
31874 Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
31875 of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
31876 address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
31877 size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
31878 AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
31879 */
31880 
31881 #if defined(MA_APPLE_MOBILE)
31882 static void ma_device__on_notification_interruption_began(ma_device* pDevice)
31883 {
31884  ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began));
31885 }
31886 
31887 static void ma_device__on_notification_interruption_ended(ma_device* pDevice)
31888 {
31889  ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended));
31890 }
31891 #endif
31892 
31893 static ma_result ma_result_from_OSStatus(OSStatus status)
31894 {
31895  switch (status)
31896  {
31897  case noErr: return MA_SUCCESS;
31898  #if defined(MA_APPLE_DESKTOP)
31899  case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
31900  case kAudioHardwareUnspecifiedError: return MA_ERROR;
31901  case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
31902  case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
31903  case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
31904  case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
31905  case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
31906  case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
31907  case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
31908  case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
31909  case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
31910  #endif
31911  default: return MA_ERROR;
31912  }
31913 }
31914 
31915 #if 0
31916 static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
31917 {
31918  switch (bit)
31919  {
31920  case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
31921  case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
31922  case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
31923  case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
31924  case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
31925  case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
31926  case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
31927  case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
31928  case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
31929  case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
31930  case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
31931  case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
31932  case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
31933  case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
31934  case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
31935  case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
31936  case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
31937  case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
31938  default: return MA_CHANNEL_NONE;
31939  }
31940 }
31941 #endif
31942 
31943 static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
31944 {
31945  MA_ASSERT(pDescription != NULL);
31946  MA_ASSERT(pFormatOut != NULL);
31947 
31948  *pFormatOut = ma_format_unknown; /* Safety. */
31949 
31950  /* There's a few things miniaudio doesn't support. */
31951  if (pDescription->mFormatID != kAudioFormatLinearPCM) {
31952  return MA_FORMAT_NOT_SUPPORTED;
31953  }
31954 
31955  /* We don't support any non-packed formats that are aligned high. */
31956  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
31957  return MA_FORMAT_NOT_SUPPORTED;
31958  }
31959 
31960  /* Only supporting native-endian. */
31961  if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
31962  return MA_FORMAT_NOT_SUPPORTED;
31963  }
31964 
31965  /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
31966  /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
31967  return MA_FORMAT_NOT_SUPPORTED;
31968  }*/
31969 
31970  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
31971  if (pDescription->mBitsPerChannel == 32) {
31972  *pFormatOut = ma_format_f32;
31973  return MA_SUCCESS;
31974  }
31975  } else {
31976  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
31977  if (pDescription->mBitsPerChannel == 16) {
31978  *pFormatOut = ma_format_s16;
31979  return MA_SUCCESS;
31980  } else if (pDescription->mBitsPerChannel == 24) {
31981  if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
31982  *pFormatOut = ma_format_s24;
31983  return MA_SUCCESS;
31984  } else {
31985  if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
31986  /* TODO: Implement ma_format_s24_32. */
31988  /*return MA_SUCCESS;*/
31989  return MA_FORMAT_NOT_SUPPORTED;
31990  }
31991  }
31992  } else if (pDescription->mBitsPerChannel == 32) {
31993  *pFormatOut = ma_format_s32;
31994  return MA_SUCCESS;
31995  }
31996  } else {
31997  if (pDescription->mBitsPerChannel == 8) {
31998  *pFormatOut = ma_format_u8;
31999  return MA_SUCCESS;
32000  }
32001  }
32002  }
32003 
32004  /* Getting here means the format is not supported. */
32005  return MA_FORMAT_NOT_SUPPORTED;
32006 }
32007 
32008 #if defined(MA_APPLE_DESKTOP)
32009 static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
32010 {
32011  switch (label)
32012  {
32013  case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
32014  case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
32015  case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
32016  case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
32017  case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
32018  case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
32019  case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
32020  case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
32021  case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
32022  case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
32023  case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
32024  case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
32025  case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
32026  case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
32027  case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
32028  case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
32029  case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
32030  case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
32031  case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
32032  case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
32033  case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
32034  case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
32035  case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
32036  case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
32037  case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
32038  case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
32039  case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
32040  case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
32041  case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
32042  case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
32043  case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
32044  case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
32045  case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
32046  case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
32047  case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
32048  case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
32049  case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
32050  case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
32051  case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
32052  case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
32053  case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
32054  case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
32055  case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
32056  case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
32057  case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
32058  case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
32059  case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
32060  case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
32061  case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
32062  case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
32063  case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
32064  case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
32065  case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
32066  case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
32067  case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
32068  case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
32069  case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
32070  case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
32071  case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
32072  case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
32073  case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
32074  case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
32075  case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
32076  case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
32077 
32078  #if 0 /* Introduced in a later version of macOS. */
32079  case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
32080  case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
32081  case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
32082  case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
32083  case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
32084  case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
32085  case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
32086  case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
32087  case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
32088  case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
32089  case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
32090  case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
32091  case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
32092  case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
32093  case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
32094  case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
32095  case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
32096  case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
32097  #endif
32098 
32099  default: return MA_CHANNEL_NONE;
32100  }
32101 }
32102 
32103 static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
32104 {
32105  MA_ASSERT(pChannelLayout != NULL);
32106 
32107  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
32108  UInt32 iChannel;
32109  for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
32110  pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
32111  }
32112  } else
32113 #if 0
32114  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
32115  /* This is the same kind of system that's used by Windows audio APIs. */
32116  UInt32 iChannel = 0;
32117  UInt32 iBit;
32118  AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
32119  for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
32120  AudioChannelBitmap bit = bitmap & (1 << iBit);
32121  if (bit != 0) {
32122  pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
32123  }
32124  }
32125  } else
32126 #endif
32127  {
32128  /*
32129  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
32130  be updated to determine the mapping based on the tag.
32131  */
32132  UInt32 channelCount;
32133 
32134  /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */
32135  if (channelMapCap > 0xFFFFFFFF) {
32136  channelMapCap = 0xFFFFFFFF;
32137  }
32138 
32139  channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);
32140 
32141  switch (pChannelLayout->mChannelLayoutTag)
32142  {
32143  case kAudioChannelLayoutTag_Mono:
32144  case kAudioChannelLayoutTag_Stereo:
32145  case kAudioChannelLayoutTag_StereoHeadphones:
32146  case kAudioChannelLayoutTag_MatrixStereo:
32147  case kAudioChannelLayoutTag_MidSide:
32148  case kAudioChannelLayoutTag_XY:
32149  case kAudioChannelLayoutTag_Binaural:
32150  case kAudioChannelLayoutTag_Ambisonic_B_Format:
32151  {
32152  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
32153  } break;
32154 
32155  case kAudioChannelLayoutTag_Octagonal:
32156  {
32157  pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
32158  pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
32159  } MA_FALLTHROUGH; /* Intentional fallthrough. */
32160  case kAudioChannelLayoutTag_Hexagonal:
32161  {
32162  pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
32163  } MA_FALLTHROUGH; /* Intentional fallthrough. */
32164  case kAudioChannelLayoutTag_Pentagonal:
32165  {
32166  pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
32167  } MA_FALLTHROUGH; /* Intentional fallthrough. */
32168  case kAudioChannelLayoutTag_Quadraphonic:
32169  {
32170  pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
32171  pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
32172  pChannelMap[1] = MA_CHANNEL_RIGHT;
32173  pChannelMap[0] = MA_CHANNEL_LEFT;
32174  } break;
32175 
32176  /* TODO: Add support for more tags here. */
32177 
32178  default:
32179  {
32180  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
32181  } break;
32182  }
32183  }
32184 
32185  return MA_SUCCESS;
32186 }
32187 
32188 #if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \
32189  (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0)
32190 #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain
32191 #else
32192 /* kAudioObjectPropertyElementMaster is deprecated. */
32193 #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster
32194 #endif
32195 
32196 static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
32197 {
32198  AudioObjectPropertyAddress propAddressDevices;
32199  UInt32 deviceObjectsDataSize;
32200  OSStatus status;
32201  AudioObjectID* pDeviceObjectIDs;
32202 
32203  MA_ASSERT(pContext != NULL);
32204  MA_ASSERT(pDeviceCount != NULL);
32205  MA_ASSERT(ppDeviceObjectIDs != NULL);
32206 
32207  /* Safety. */
32208  *pDeviceCount = 0;
32209  *ppDeviceObjectIDs = NULL;
32210 
32211  propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
32212  propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
32213  propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32214 
32215  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
32216  if (status != noErr) {
32217  return ma_result_from_OSStatus(status);
32218  }
32219 
32220  pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
32221  if (pDeviceObjectIDs == NULL) {
32222  return MA_OUT_OF_MEMORY;
32223  }
32224 
32225  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
32226  if (status != noErr) {
32227  ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32228  return ma_result_from_OSStatus(status);
32229  }
32230 
32231  *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
32232  *ppDeviceObjectIDs = pDeviceObjectIDs;
32233 
32234  return MA_SUCCESS;
32235 }
32236 
32237 static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
32238 {
32239  AudioObjectPropertyAddress propAddress;
32240  UInt32 dataSize;
32241  OSStatus status;
32242 
32243  MA_ASSERT(pContext != NULL);
32244 
32245  propAddress.mSelector = kAudioDevicePropertyDeviceUID;
32246  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
32247  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32248 
32249  dataSize = sizeof(*pUID);
32250  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
32251  if (status != noErr) {
32252  return ma_result_from_OSStatus(status);
32253  }
32254 
32255  return MA_SUCCESS;
32256 }
32257 
32258 static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
32259 {
32260  CFStringRef uid;
32261  ma_result result;
32262 
32263  MA_ASSERT(pContext != NULL);
32264 
32265  result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
32266  if (result != MA_SUCCESS) {
32267  return result;
32268  }
32269 
32270  if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
32271  return MA_ERROR;
32272  }
32273 
32274  ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
32275  return MA_SUCCESS;
32276 }
32277 
32278 static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
32279 {
32280  AudioObjectPropertyAddress propAddress;
32281  CFStringRef deviceName = NULL;
32282  UInt32 dataSize;
32283  OSStatus status;
32284 
32285  MA_ASSERT(pContext != NULL);
32286 
32287  propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
32288  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
32289  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32290 
32291  dataSize = sizeof(deviceName);
32292  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
32293  if (status != noErr) {
32294  return ma_result_from_OSStatus(status);
32295  }
32296 
32297  if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
32298  return MA_ERROR;
32299  }
32300 
32301  ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
32302  return MA_SUCCESS;
32303 }
32304 
32305 static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
32306 {
32307  AudioObjectPropertyAddress propAddress;
32308  UInt32 dataSize;
32309  OSStatus status;
32310  AudioBufferList* pBufferList;
32311  ma_bool32 isSupported;
32312 
32313  MA_ASSERT(pContext != NULL);
32314 
32315  /* 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. */
32316  propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
32317  propAddress.mScope = scope;
32318  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32319 
32320  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
32321  if (status != noErr) {
32322  return MA_FALSE;
32323  }
32324 
32325  pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks);
32326  if (pBufferList == NULL) {
32327  return MA_FALSE; /* Out of memory. */
32328  }
32329 
32330  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
32331  if (status != noErr) {
32332  ma_free(pBufferList, &pContext->allocationCallbacks);
32333  return MA_FALSE;
32334  }
32335 
32336  isSupported = MA_FALSE;
32337  if (pBufferList->mNumberBuffers > 0) {
32338  isSupported = MA_TRUE;
32339  }
32340 
32341  ma_free(pBufferList, &pContext->allocationCallbacks);
32342  return isSupported;
32343 }
32344 
32345 static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
32346 {
32347  return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
32348 }
32349 
32350 static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
32351 {
32352  return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
32353 }
32354 
32355 
32356 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(). */
32357 {
32358  AudioObjectPropertyAddress propAddress;
32359  UInt32 dataSize;
32360  OSStatus status;
32361  AudioStreamRangedDescription* pDescriptions;
32362 
32363  MA_ASSERT(pContext != NULL);
32364  MA_ASSERT(pDescriptionCount != NULL);
32365  MA_ASSERT(ppDescriptions != NULL);
32366 
32367  /*
32368  TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
32369  MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
32370  */
32371  propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
32372  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32373  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32374 
32375  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
32376  if (status != noErr) {
32377  return ma_result_from_OSStatus(status);
32378  }
32379 
32380  pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
32381  if (pDescriptions == NULL) {
32382  return MA_OUT_OF_MEMORY;
32383  }
32384 
32385  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
32386  if (status != noErr) {
32387  ma_free(pDescriptions, &pContext->allocationCallbacks);
32388  return ma_result_from_OSStatus(status);
32389  }
32390 
32391  *pDescriptionCount = dataSize / sizeof(*pDescriptions);
32392  *ppDescriptions = pDescriptions;
32393  return MA_SUCCESS;
32394 }
32395 
32396 
32397 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(). */
32398 {
32399  AudioObjectPropertyAddress propAddress;
32400  UInt32 dataSize;
32401  OSStatus status;
32402  AudioChannelLayout* pChannelLayout;
32403 
32404  MA_ASSERT(pContext != NULL);
32405  MA_ASSERT(ppChannelLayout != NULL);
32406 
32407  *ppChannelLayout = NULL; /* Safety. */
32408 
32409  propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
32410  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32411  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32412 
32413  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
32414  if (status != noErr) {
32415  return ma_result_from_OSStatus(status);
32416  }
32417 
32418  pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
32419  if (pChannelLayout == NULL) {
32420  return MA_OUT_OF_MEMORY;
32421  }
32422 
32423  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
32424  if (status != noErr) {
32425  ma_free(pChannelLayout, &pContext->allocationCallbacks);
32426  return ma_result_from_OSStatus(status);
32427  }
32428 
32429  *ppChannelLayout = pChannelLayout;
32430  return MA_SUCCESS;
32431 }
32432 
32433 static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
32434 {
32435  AudioChannelLayout* pChannelLayout;
32436  ma_result result;
32437 
32438  MA_ASSERT(pContext != NULL);
32439  MA_ASSERT(pChannelCount != NULL);
32440 
32441  *pChannelCount = 0; /* Safety. */
32442 
32443  result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
32444  if (result != MA_SUCCESS) {
32445  return result;
32446  }
32447 
32448  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
32449  *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
32450  } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
32451  *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
32452  } else {
32453  *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
32454  }
32455 
32456  ma_free(pChannelLayout, &pContext->allocationCallbacks);
32457  return MA_SUCCESS;
32458 }
32459 
32460 #if 0
32461 static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
32462 {
32463  AudioChannelLayout* pChannelLayout;
32464  ma_result result;
32465 
32466  MA_ASSERT(pContext != NULL);
32467 
32468  result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
32469  if (result != MA_SUCCESS) {
32470  return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
32471  }
32472 
32473  result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
32474  if (result != MA_SUCCESS) {
32475  ma_free(pChannelLayout, &pContext->allocationCallbacks);
32476  return result;
32477  }
32478 
32479  ma_free(pChannelLayout, &pContext->allocationCallbacks);
32480  return result;
32481 }
32482 #endif
32483 
32484 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(). */
32485 {
32486  AudioObjectPropertyAddress propAddress;
32487  UInt32 dataSize;
32488  OSStatus status;
32489  AudioValueRange* pSampleRateRanges;
32490 
32491  MA_ASSERT(pContext != NULL);
32492  MA_ASSERT(pSampleRateRangesCount != NULL);
32493  MA_ASSERT(ppSampleRateRanges != NULL);
32494 
32495  /* Safety. */
32496  *pSampleRateRangesCount = 0;
32497  *ppSampleRateRanges = NULL;
32498 
32499  propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
32500  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32501  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32502 
32503  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
32504  if (status != noErr) {
32505  return ma_result_from_OSStatus(status);
32506  }
32507 
32508  pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
32509  if (pSampleRateRanges == NULL) {
32510  return MA_OUT_OF_MEMORY;
32511  }
32512 
32513  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
32514  if (status != noErr) {
32515  ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32516  return ma_result_from_OSStatus(status);
32517  }
32518 
32519  *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
32520  *ppSampleRateRanges = pSampleRateRanges;
32521  return MA_SUCCESS;
32522 }
32523 
32524 #if 0
32525 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)
32526 {
32527  UInt32 sampleRateRangeCount;
32528  AudioValueRange* pSampleRateRanges;
32529  ma_result result;
32530 
32531  MA_ASSERT(pContext != NULL);
32532  MA_ASSERT(pSampleRateOut != NULL);
32533 
32534  *pSampleRateOut = 0; /* Safety. */
32535 
32536  result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
32537  if (result != MA_SUCCESS) {
32538  return result;
32539  }
32540 
32541  if (sampleRateRangeCount == 0) {
32542  ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32543  return MA_ERROR; /* Should never hit this case should we? */
32544  }
32545 
32546  if (sampleRateIn == 0) {
32547  /* Search in order of miniaudio's preferred priority. */
32548  UInt32 iMALSampleRate;
32549  for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
32550  ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
32551  UInt32 iCASampleRate;
32552  for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
32553  AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
32554  if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
32555  *pSampleRateOut = malSampleRate;
32556  ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32557  return MA_SUCCESS;
32558  }
32559  }
32560  }
32561 
32562  /*
32563  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
32564  case we just fall back to the first one reported by Core Audio.
32565  */
32566  MA_ASSERT(sampleRateRangeCount > 0);
32567 
32568  *pSampleRateOut = pSampleRateRanges[0].mMinimum;
32569  ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32570  return MA_SUCCESS;
32571  } else {
32572  /* Find the closest match to this sample rate. */
32573  UInt32 currentAbsoluteDifference = INT32_MAX;
32574  UInt32 iCurrentClosestRange = (UInt32)-1;
32575  UInt32 iRange;
32576  for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
32577  if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
32578  *pSampleRateOut = sampleRateIn;
32579  ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32580  return MA_SUCCESS;
32581  } else {
32582  UInt32 absoluteDifference;
32583  if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
32584  absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
32585  } else {
32586  absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
32587  }
32588 
32589  if (currentAbsoluteDifference > absoluteDifference) {
32590  currentAbsoluteDifference = absoluteDifference;
32591  iCurrentClosestRange = iRange;
32592  }
32593  }
32594  }
32595 
32596  MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
32597 
32598  *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
32599  ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32600  return MA_SUCCESS;
32601  }
32602 
32603  /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
32604  /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/
32605  /*return MA_ERROR;*/
32606 }
32607 #endif
32608 
32609 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)
32610 {
32611  AudioObjectPropertyAddress propAddress;
32612  AudioValueRange bufferSizeRange;
32613  UInt32 dataSize;
32614  OSStatus status;
32615 
32616  MA_ASSERT(pContext != NULL);
32617  MA_ASSERT(pBufferSizeInFramesOut != NULL);
32618 
32619  *pBufferSizeInFramesOut = 0; /* Safety. */
32620 
32621  propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
32622  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32623  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32624 
32625  dataSize = sizeof(bufferSizeRange);
32626  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
32627  if (status != noErr) {
32628  return ma_result_from_OSStatus(status);
32629  }
32630 
32631  /* This is just a clamp. */
32632  if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
32633  *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
32634  } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
32635  *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
32636  } else {
32637  *pBufferSizeInFramesOut = bufferSizeInFramesIn;
32638  }
32639 
32640  return MA_SUCCESS;
32641 }
32642 
32643 static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
32644 {
32645  ma_result result;
32646  ma_uint32 chosenBufferSizeInFrames;
32647  AudioObjectPropertyAddress propAddress;
32648  UInt32 dataSize;
32649  OSStatus status;
32650 
32651  MA_ASSERT(pContext != NULL);
32652 
32653  result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
32654  if (result != MA_SUCCESS) {
32655  return result;
32656  }
32657 
32658  /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
32659  propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
32660  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32661  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32662 
32663  ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
32664 
32665  /* Get the actual size of the buffer. */
32666  dataSize = sizeof(*pPeriodSizeInOut);
32667  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
32668  if (status != noErr) {
32669  return ma_result_from_OSStatus(status);
32670  }
32671 
32672  *pPeriodSizeInOut = chosenBufferSizeInFrames;
32673  return MA_SUCCESS;
32674 }
32675 
32676 static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID)
32677 {
32678  AudioObjectPropertyAddress propAddressDefaultDevice;
32679  UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
32680  AudioObjectID defaultDeviceObjectID;
32681  OSStatus status;
32682 
32683  MA_ASSERT(pContext != NULL);
32684  MA_ASSERT(pDeviceObjectID != NULL);
32685 
32686  /* Safety. */
32687  *pDeviceObjectID = 0;
32688 
32689  propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
32690  propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32691  if (deviceType == ma_device_type_playback) {
32692  propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
32693  } else {
32694  propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
32695  }
32696 
32697  defaultDeviceObjectIDSize = sizeof(AudioObjectID);
32698  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
32699  if (status == noErr) {
32700  *pDeviceObjectID = defaultDeviceObjectID;
32701  return MA_SUCCESS;
32702  }
32703 
32704  /* If we get here it means we couldn't find the device. */
32705  return MA_NO_DEVICE;
32706 }
32707 
32708 static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
32709 {
32710  MA_ASSERT(pContext != NULL);
32711  MA_ASSERT(pDeviceObjectID != NULL);
32712 
32713  /* Safety. */
32714  *pDeviceObjectID = 0;
32715 
32716  if (pDeviceID == NULL) {
32717  /* Default device. */
32718  return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID);
32719  } else {
32720  /* Explicit device. */
32721  UInt32 deviceCount;
32722  AudioObjectID* pDeviceObjectIDs;
32723  ma_result result;
32724  UInt32 iDevice;
32725 
32726  result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
32727  if (result != MA_SUCCESS) {
32728  return result;
32729  }
32730 
32731  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
32732  AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
32733 
32734  char uid[256];
32735  if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
32736  continue;
32737  }
32738 
32739  if (deviceType == ma_device_type_playback) {
32740  if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
32741  if (strcmp(uid, pDeviceID->coreaudio) == 0) {
32742  *pDeviceObjectID = deviceObjectID;
32743  ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32744  return MA_SUCCESS;
32745  }
32746  }
32747  } else {
32748  if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
32749  if (strcmp(uid, pDeviceID->coreaudio) == 0) {
32750  *pDeviceObjectID = deviceObjectID;
32751  ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32752  return MA_SUCCESS;
32753  }
32754  }
32755  }
32756  }
32757 
32758  ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32759  }
32760 
32761  /* If we get here it means we couldn't find the device. */
32762  return MA_NO_DEVICE;
32763 }
32764 
32765 
32766 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)
32767 {
32768  UInt32 deviceFormatDescriptionCount;
32769  AudioStreamRangedDescription* pDeviceFormatDescriptions;
32770  ma_result result;
32771  ma_uint32 desiredSampleRate;
32772  ma_uint32 desiredChannelCount;
32773  ma_format desiredFormat;
32774  AudioStreamBasicDescription bestDeviceFormatSoFar;
32775  ma_bool32 hasSupportedFormat;
32776  UInt32 iFormat;
32777 
32778  result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
32779  if (result != MA_SUCCESS) {
32780  return result;
32781  }
32782 
32783  desiredSampleRate = sampleRate;
32784  if (desiredSampleRate == 0) {
32785  desiredSampleRate = pOrigFormat->mSampleRate;
32786  }
32787 
32788  desiredChannelCount = channels;
32789  if (desiredChannelCount == 0) {
32790  desiredChannelCount = pOrigFormat->mChannelsPerFrame;
32791  }
32792 
32793  desiredFormat = format;
32794  if (desiredFormat == ma_format_unknown) {
32795  result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat);
32796  if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) {
32797  desiredFormat = g_maFormatPriorities[0];
32798  }
32799  }
32800 
32801  /*
32802  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
32803  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.
32804  */
32805  MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
32806 
32807  hasSupportedFormat = MA_FALSE;
32808  for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
32809  ma_format formatFromDescription;
32810  ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription);
32811  if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) {
32812  hasSupportedFormat = MA_TRUE;
32813  bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
32814  break;
32815  }
32816  }
32817 
32818  if (!hasSupportedFormat) {
32819  ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
32820  return MA_FORMAT_NOT_SUPPORTED;
32821  }
32822 
32823 
32824  for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
32825  AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
32826  ma_format thisSampleFormat;
32827  ma_result formatResult;
32828  ma_format bestSampleFormatSoFar;
32829 
32830  /* If the format is not supported by miniaudio we need to skip this one entirely. */
32831  formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
32832  if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
32833  continue; /* The format is not supported by miniaudio. Skip. */
32834  }
32835 
32836  ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
32837 
32838  /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
32839  if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
32840  /*
32841  The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
32842  so far has an equal sample rate we can just ignore this one.
32843  */
32844  if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
32845  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. */
32846  } else {
32847  /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
32848  if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
32849  /* This format has a different sample rate _and_ a different channel count. */
32850  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
32851  continue; /* No change to the best format. */
32852  } else {
32853  /*
32854  Both this format and the best so far have different sample rates and different channel counts. Whichever has the
32855  best format is the new best.
32856  */
32857  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
32858  bestDeviceFormatSoFar = thisDeviceFormat;
32859  continue;
32860  } else {
32861  continue; /* No change to the best format. */
32862  }
32863  }
32864  } else {
32865  /* This format has a different sample rate but the desired channel count. */
32866  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
32867  /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
32868  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
32869  bestDeviceFormatSoFar = thisDeviceFormat;
32870  continue;
32871  } else {
32872  continue; /* No change to the best format for now. */
32873  }
32874  } else {
32875  /* This format has the desired channel count, but the best so far does not. We have a new best. */
32876  bestDeviceFormatSoFar = thisDeviceFormat;
32877  continue;
32878  }
32879  }
32880  }
32881  } else {
32882  /*
32883  The sample rates match which makes this format a very high priority contender. If the best format so far has a different
32884  sample rate it needs to be replaced with this one.
32885  */
32886  if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
32887  bestDeviceFormatSoFar = thisDeviceFormat;
32888  continue;
32889  } else {
32890  /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
32891  if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
32892  /*
32893  In this case this format has the same channel count as what the client is requesting. If the best format so far has
32894  a different count, this one becomes the new best.
32895  */
32896  if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
32897  bestDeviceFormatSoFar = thisDeviceFormat;
32898  continue;
32899  } else {
32900  /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
32901  if (thisSampleFormat == desiredFormat) {
32902  bestDeviceFormatSoFar = thisDeviceFormat;
32903  break; /* Found the exact match. */
32904  } else {
32905  /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
32906  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
32907  bestDeviceFormatSoFar = thisDeviceFormat;
32908  continue;
32909  } else {
32910  continue; /* No change to the best format for now. */
32911  }
32912  }
32913  }
32914  } else {
32915  /*
32916  In this case the channel count is different to what the client has requested. If the best so far has the same channel
32917  count as the requested count then it remains the best.
32918  */
32919  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
32920  continue;
32921  } else {
32922  /*
32923  This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
32924  the same priority, but we need to compare the format now.
32925  */
32926  if (thisSampleFormat == bestSampleFormatSoFar) {
32927  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
32928  bestDeviceFormatSoFar = thisDeviceFormat;
32929  continue;
32930  } else {
32931  continue; /* No change to the best format for now. */
32932  }
32933  }
32934  }
32935  }
32936  }
32937  }
32938  }
32939 
32940  *pFormat = bestDeviceFormatSoFar;
32941 
32942  ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
32943  return MA_SUCCESS;
32944 }
32945 
32946 static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
32947 {
32948  AudioUnitScope deviceScope;
32949  AudioUnitElement deviceBus;
32950  UInt32 channelLayoutSize;
32951  OSStatus status;
32952  AudioChannelLayout* pChannelLayout;
32953  ma_result result;
32954 
32955  MA_ASSERT(pContext != NULL);
32956 
32957  if (deviceType == ma_device_type_playback) {
32958  deviceScope = kAudioUnitScope_Input;
32959  deviceBus = MA_COREAUDIO_OUTPUT_BUS;
32960  } else {
32961  deviceScope = kAudioUnitScope_Output;
32962  deviceBus = MA_COREAUDIO_INPUT_BUS;
32963  }
32964 
32965  status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
32966  if (status != noErr) {
32967  return ma_result_from_OSStatus(status);
32968  }
32969 
32970  pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks);
32971  if (pChannelLayout == NULL) {
32972  return MA_OUT_OF_MEMORY;
32973  }
32974 
32975  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
32976  if (status != noErr) {
32977  ma_free(pChannelLayout, &pContext->allocationCallbacks);
32978  return ma_result_from_OSStatus(status);
32979  }
32980 
32981  result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
32982  if (result != MA_SUCCESS) {
32983  ma_free(pChannelLayout, &pContext->allocationCallbacks);
32984  return result;
32985  }
32986 
32987  ma_free(pChannelLayout, &pContext->allocationCallbacks);
32988  return MA_SUCCESS;
32989 }
32990 #endif /* MA_APPLE_DESKTOP */
32991 
32992 
32993 #if !defined(MA_APPLE_DESKTOP)
32994 static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
32995 {
32996  MA_ZERO_OBJECT(pInfo);
32997  ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1);
32998  ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1);
32999 }
33000 #endif
33001 
33002 static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
33003 {
33004 #if defined(MA_APPLE_DESKTOP)
33005  UInt32 deviceCount;
33006  AudioObjectID* pDeviceObjectIDs;
33007  AudioObjectID defaultDeviceObjectIDPlayback;
33008  AudioObjectID defaultDeviceObjectIDCapture;
33009  ma_result result;
33010  UInt32 iDevice;
33011 
33012  ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
33013  ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
33014 
33015  result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
33016  if (result != MA_SUCCESS) {
33017  return result;
33018  }
33019 
33020  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
33021  AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
33022  ma_device_info info;
33023 
33024  MA_ZERO_OBJECT(&info);
33025  if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
33026  continue;
33027  }
33028  if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
33029  continue;
33030  }
33031 
33032  if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
33033  if (deviceObjectID == defaultDeviceObjectIDPlayback) {
33034  info.isDefault = MA_TRUE;
33035  }
33036 
33037  if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
33038  break;
33039  }
33040  }
33041  if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
33042  if (deviceObjectID == defaultDeviceObjectIDCapture) {
33043  info.isDefault = MA_TRUE;
33044  }
33045 
33046  if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
33047  break;
33048  }
33049  }
33050  }
33051 
33052  ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
33053 #else
33054  ma_device_info info;
33055  NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
33056  NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
33057 
33058  for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
33059  ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
33060  if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
33061  return MA_SUCCESS;
33062  }
33063  }
33064 
33065  for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
33066  ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
33067  if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
33068  return MA_SUCCESS;
33069  }
33070  }
33071 #endif
33072 
33073  return MA_SUCCESS;
33074 }
33075 
33076 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)
33077 {
33078  ma_result result;
33079 
33080  MA_ASSERT(pContext != NULL);
33081 
33082 #if defined(MA_APPLE_DESKTOP)
33083  /* Desktop */
33084  {
33085  AudioObjectID deviceObjectID;
33086  AudioObjectID defaultDeviceObjectID;
33087  UInt32 streamDescriptionCount;
33088  AudioStreamRangedDescription* pStreamDescriptions;
33089  UInt32 iStreamDescription;
33090  UInt32 sampleRateRangeCount;
33091  AudioValueRange* pSampleRateRanges;
33092 
33093  ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */
33094 
33095  result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
33096  if (result != MA_SUCCESS) {
33097  return result;
33098  }
33099 
33100  result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
33101  if (result != MA_SUCCESS) {
33102  return result;
33103  }
33104 
33105  result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
33106  if (result != MA_SUCCESS) {
33107  return result;
33108  }
33109 
33110  if (deviceObjectID == defaultDeviceObjectID) {
33111  pDeviceInfo->isDefault = MA_TRUE;
33112  }
33113 
33114  /*
33115  There could be a large number of permutations here. Fortunately there is only a single channel count
33116  being reported which reduces this quite a bit. For sample rates we're only reporting those that are
33117  one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into
33118  our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen
33119  if some driver performs software data conversion and therefore reports every possible format and
33120  sample rate.
33121  */
33122  pDeviceInfo->nativeDataFormatCount = 0;
33123 
33124  /* Formats. */
33125  {
33126  ma_format uniqueFormats[ma_format_count];
33127  ma_uint32 uniqueFormatCount = 0;
33128  ma_uint32 channels;
33129 
33130  /* Channels. */
33131  result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
33132  if (result != MA_SUCCESS) {
33133  return result;
33134  }
33135 
33136  /* Formats. */
33137  result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
33138  if (result != MA_SUCCESS) {
33139  return result;
33140  }
33141 
33142  for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
33143  ma_format format;
33144  ma_bool32 hasFormatBeenHandled = MA_FALSE;
33145  ma_uint32 iOutputFormat;
33146  ma_uint32 iSampleRate;
33147 
33148  result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
33149  if (result != MA_SUCCESS) {
33150  continue;
33151  }
33152 
33153  MA_ASSERT(format != ma_format_unknown);
33154 
33155  /* Make sure the format isn't already in the output list. */
33156  for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
33157  if (uniqueFormats[iOutputFormat] == format) {
33158  hasFormatBeenHandled = MA_TRUE;
33159  break;
33160  }
33161  }
33162 
33163  /* If we've already handled this format just skip it. */
33164  if (hasFormatBeenHandled) {
33165  continue;
33166  }
33167 
33168  uniqueFormats[uniqueFormatCount] = format;
33169  uniqueFormatCount += 1;
33170 
33171  /* Sample Rates */
33172  result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
33173  if (result != MA_SUCCESS) {
33174  return result;
33175  }
33176 
33177  /*
33178  Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
33179  between this range.
33180  */
33181  for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
33182  ma_uint32 iStandardSampleRate;
33183  for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
33184  ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
33185  if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
33186  /* We have a new data format. Add it to the list. */
33187  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
33188  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
33189  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
33190  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
33191  pDeviceInfo->nativeDataFormatCount += 1;
33192 
33193  if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
33194  break; /* No more room for any more formats. */
33195  }
33196  }
33197  }
33198  }
33199 
33200  ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
33201 
33202  if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
33203  break; /* No more room for any more formats. */
33204  }
33205  }
33206 
33207  ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
33208  }
33209  }
33210 #else
33211  /* Mobile */
33212  {
33213  AudioComponentDescription desc;
33214  AudioComponent component;
33215  AudioUnit audioUnit;
33216  OSStatus status;
33217  AudioUnitScope formatScope;
33218  AudioUnitElement formatElement;
33219  AudioStreamBasicDescription bestFormat;
33220  UInt32 propSize;
33221 
33222  /* We want to ensure we use a consistent device name to device enumeration. */
33223  if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') {
33224  ma_bool32 found = MA_FALSE;
33225  if (deviceType == ma_device_type_playback) {
33226  NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
33227  for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
33228  if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
33229  ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
33230  found = MA_TRUE;
33231  break;
33232  }
33233  }
33234  } else {
33235  NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
33236  for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
33237  if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
33238  ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
33239  found = MA_TRUE;
33240  break;
33241  }
33242  }
33243  }
33244 
33245  if (!found) {
33246  return MA_DOES_NOT_EXIST;
33247  }
33248  } else {
33249  if (deviceType == ma_device_type_playback) {
33250  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
33251  } else {
33252  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
33253  }
33254  }
33255 
33256 
33257  /*
33258  Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
33259  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
33260  retrieve from the AVAudioSession shared instance.
33261  */
33262  desc.componentType = kAudioUnitType_Output;
33263  desc.componentSubType = kAudioUnitSubType_RemoteIO;
33264  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
33265  desc.componentFlags = 0;
33266  desc.componentFlagsMask = 0;
33267 
33268  component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
33269  if (component == NULL) {
33270  return MA_FAILED_TO_INIT_BACKEND;
33271  }
33272 
33273  status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
33274  if (status != noErr) {
33275  return ma_result_from_OSStatus(status);
33276  }
33277 
33278  formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
33279  formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
33280 
33281  propSize = sizeof(bestFormat);
33282  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
33283  if (status != noErr) {
33284  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
33285  return ma_result_from_OSStatus(status);
33286  }
33287 
33288  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
33289  audioUnit = NULL;
33290 
33291  /* Only a single format is being reported for iOS. */
33292  pDeviceInfo->nativeDataFormatCount = 1;
33293 
33294  result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
33295  if (result != MA_SUCCESS) {
33296  return result;
33297  }
33298 
33299  pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
33300 
33301  /*
33302  It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
33303  this we just get the shared instance and inspect.
33304  */
33305  @autoreleasepool {
33306  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
33307  MA_ASSERT(pAudioSession != NULL);
33308 
33309  pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
33310  }
33311  }
33312 #endif
33313 
33314  (void)pDeviceInfo; /* Unused. */
33315  return MA_SUCCESS;
33316 }
33317 
33318 static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)
33319 {
33320  AudioBufferList* pBufferList;
33321  UInt32 audioBufferSizeInBytes;
33322  size_t allocationSize;
33323 
33324  MA_ASSERT(sizeInFrames > 0);
33325  MA_ASSERT(format != ma_format_unknown);
33326  MA_ASSERT(channels > 0);
33327 
33328  allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
33329  if (layout == ma_stream_layout_interleaved) {
33330  /* Interleaved case. This is the simple case because we just have one buffer. */
33331  allocationSize += sizeof(AudioBuffer) * 1;
33332  } else {
33333  /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
33334  allocationSize += sizeof(AudioBuffer) * channels;
33335  }
33336 
33337  allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);
33338 
33339  pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks);
33340  if (pBufferList == NULL) {
33341  return NULL;
33342  }
33343 
33344  audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format));
33345 
33346  if (layout == ma_stream_layout_interleaved) {
33347  pBufferList->mNumberBuffers = 1;
33348  pBufferList->mBuffers[0].mNumberChannels = channels;
33349  pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels;
33350  pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
33351  } else {
33352  ma_uint32 iBuffer;
33353  pBufferList->mNumberBuffers = channels;
33354  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
33355  pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
33356  pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes;
33357  pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer);
33358  }
33359  }
33360 
33361  return pBufferList;
33362 }
33363 
33364 static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout)
33365 {
33366  MA_ASSERT(pDevice != NULL);
33367  MA_ASSERT(format != ma_format_unknown);
33368  MA_ASSERT(channels > 0);
33369 
33370  /* Only resize the buffer if necessary. */
33371  if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {
33372  AudioBufferList* pNewAudioBufferList;
33373 
33374  pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
33375  if (pNewAudioBufferList == NULL) {
33376  return MA_OUT_OF_MEMORY;
33377  }
33378 
33379  /* At this point we'll have a new AudioBufferList and we can free the old one. */
33380  ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
33381  pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;
33382  pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;
33383  }
33384 
33385  /* Getting here means the capacity of the audio is fine. */
33386  return MA_SUCCESS;
33387 }
33388 
33389 
33390 static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
33391 {
33392  ma_device* pDevice = (ma_device*)pUserData;
33393  ma_stream_layout layout;
33394 
33395  MA_ASSERT(pDevice != NULL);
33396 
33397  /*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);*/
33398 
33399  /* 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. */
33400  layout = ma_stream_layout_interleaved;
33401  if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
33402  layout = ma_stream_layout_deinterleaved;
33403  }
33404 
33405  if (layout == ma_stream_layout_interleaved) {
33406  /* For now we can assume everything is interleaved. */
33407  UInt32 iBuffer;
33408  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
33409  if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
33410  ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
33411  if (frameCountForThisBuffer > 0) {
33412  ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);
33413  }
33414 
33415  /*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);*/
33416  } else {
33417  /*
33418  This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
33419  not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
33420  output silence here.
33421  */
33422  MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
33423  /*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);*/
33424  }
33425  }
33426  } else {
33427  /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
33428  MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */
33429 
33430  /*
33431  For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
33432  very strange has happened and we're not going to support it.
33433  */
33434  if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
33435  ma_uint8 tempBuffer[4096];
33436  UInt32 iBuffer;
33437 
33438  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
33439  ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
33440  ma_uint32 framesRemaining = frameCountPerBuffer;
33441 
33442  while (framesRemaining > 0) {
33443  void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
33444  ma_uint32 iChannel;
33445  ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
33446  if (framesToRead > framesRemaining) {
33447  framesToRead = framesRemaining;
33448  }
33449 
33450  ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
33451 
33452  for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
33453  ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
33454  }
33455 
33456  ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
33457 
33458  framesRemaining -= framesToRead;
33459  }
33460  }
33461  }
33462  }
33463 
33464  (void)pActionFlags;
33465  (void)pTimeStamp;
33466  (void)busNumber;
33467  (void)frameCount;
33468 
33469  return noErr;
33470 }
33471 
33472 static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
33473 {
33474  ma_device* pDevice = (ma_device*)pUserData;
33475  AudioBufferList* pRenderedBufferList;
33476  ma_result result;
33477  ma_stream_layout layout;
33478  ma_uint32 iBuffer;
33479  OSStatus status;
33480 
33481  MA_ASSERT(pDevice != NULL);
33482 
33483  pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
33484  MA_ASSERT(pRenderedBufferList);
33485 
33486  /* 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. */
33487  layout = ma_stream_layout_interleaved;
33488  if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
33489  layout = ma_stream_layout_deinterleaved;
33490  }
33491 
33492  /*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);*/
33493 
33494  /*
33495  There has been a situation reported where frame count passed into this function is greater than the capacity of
33496  our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be,
33497  so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the
33498  number of frames requested by this callback.
33499  */
33500  result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);
33501  if (result != MA_SUCCESS) {
33502  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n");
33503  return noErr;
33504  }
33505 
33506  pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
33507  MA_ASSERT(pRenderedBufferList);
33508 
33509  /*
33510  When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes
33511  that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer
33512  being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a
33513  problem when a future call to this callback specifies a larger number of frames.
33514 
33515  To work around this we need to explicitly set the size of each buffer to their respective size in bytes.
33516  */
33517  for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
33518  pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
33519  }
33520 
33521  status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
33522  if (status != noErr) {
33523  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status);
33524  return status;
33525  }
33526 
33527  if (layout == ma_stream_layout_interleaved) {
33528  for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
33529  if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
33530  ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);
33531  /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/
33532  } else {
33533  /*
33534  This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
33535  not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
33536  */
33537  ma_uint8 silentBuffer[4096];
33538  ma_uint32 framesRemaining;
33539 
33540  MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
33541 
33542  framesRemaining = frameCount;
33543  while (framesRemaining > 0) {
33544  ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
33545  if (framesToSend > framesRemaining) {
33546  framesToSend = framesRemaining;
33547  }
33548 
33549  ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
33550 
33551  framesRemaining -= framesToSend;
33552  }
33553 
33554  /*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);*/
33555  }
33556  }
33557  } else {
33558  /* 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. */
33559  MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */
33560 
33561  /*
33562  For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
33563  very strange has happened and we're not going to support it.
33564  */
33565  if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
33566  ma_uint8 tempBuffer[4096];
33567  for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
33568  ma_uint32 framesRemaining = frameCount;
33569  while (framesRemaining > 0) {
33570  void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
33571  ma_uint32 iChannel;
33572  ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
33573  if (framesToSend > framesRemaining) {
33574  framesToSend = framesRemaining;
33575  }
33576 
33577  for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
33578  ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
33579  }
33580 
33581  ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
33582  ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);
33583 
33584  framesRemaining -= framesToSend;
33585  }
33586  }
33587  }
33588  }
33589 
33590  (void)pActionFlags;
33591  (void)pTimeStamp;
33592  (void)busNumber;
33593  (void)frameCount;
33594  (void)pUnusedBufferList;
33595 
33596  return noErr;
33597 }
33598 
33599 static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
33600 {
33601  ma_device* pDevice = (ma_device*)pUserData;
33602  MA_ASSERT(pDevice != NULL);
33603 
33604  /* Don't do anything if it looks like we're just reinitializing due to a device switch. */
33605  if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
33606  ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
33607  return;
33608  }
33609 
33610  /*
33611  There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
33612  AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
33613  can try waiting on the same lock. I'm going to try working around this by not calling any Core
33614  Audio APIs in the callback when the device has been stopped or uninitialized.
33615  */
33616  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) {
33617  ma_device__on_notification_stopped(pDevice);
33618  } else {
33619  UInt32 isRunning;
33620  UInt32 isRunningSize = sizeof(isRunning);
33621  OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
33622  if (status != noErr) {
33623  goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */
33624  }
33625 
33626  if (!isRunning) {
33627  /*
33628  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:
33629 
33630  1) When the device is unplugged, this will be called _before_ the default device change notification.
33631  2) When the device is changed via the default device change notification, this will be called _after_ the switch.
33632 
33633  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.
33634  */
33635  if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
33636  ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
33637  /*
33638  It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
33639  via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
33640  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
33641  hasn't!).
33642  */
33643  if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
33644  ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
33645  goto done;
33646  }
33647 
33648  /*
33649  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
33650  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
33651  likely be successful in switching to the new device.
33652 
33653  TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted.
33654  */
33655  goto done;
33656  }
33657 
33658  /* Getting here means we need to stop the device. */
33659  ma_device__on_notification_stopped(pDevice);
33660  }
33661  }
33662 
33663  (void)propertyID; /* Unused. */
33664 
33665 done:
33666  /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */
33667  ma_event_signal(&pDevice->coreaudio.stopEvent);
33668 }
33669 
33670 #if defined(MA_APPLE_DESKTOP)
33671 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. */
33672 static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
33673 static ma_mutex g_DeviceTrackingMutex_CoreAudio;
33674 static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
33675 static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
33676 static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
33677 
33678 static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
33679 {
33680  ma_device_type deviceType;
33681 
33682  /* Not sure if I really need to check this, but it makes me feel better. */
33683  if (addressCount == 0) {
33684  return noErr;
33685  }
33686 
33687  if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
33688  deviceType = ma_device_type_playback;
33689  } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
33690  deviceType = ma_device_type_capture;
33691  } else {
33692  return noErr; /* Should never hit this. */
33693  }
33694 
33695  ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
33696  {
33697  ma_uint32 iDevice;
33698  for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
33699  ma_result reinitResult;
33700  ma_device* pDevice;
33701 
33702  pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
33703  if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
33704  if (deviceType == ma_device_type_playback) {
33705  pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
33706  reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
33707  pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
33708  } else {
33709  pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
33710  reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
33711  pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
33712  }
33713 
33714  if (reinitResult == MA_SUCCESS) {
33715  ma_device__post_init_setup(pDevice, deviceType);
33716 
33717  /* Restart the device if required. If this fails we need to stop the device entirely. */
33718  if (ma_device_get_state(pDevice) == ma_device_state_started) {
33719  OSStatus status;
33720  if (deviceType == ma_device_type_playback) {
33721  status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
33722  if (status != noErr) {
33723  if (pDevice->type == ma_device_type_duplex) {
33724  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33725  }
33726  ma_device__set_state(pDevice, ma_device_state_stopped);
33727  }
33728  } else if (deviceType == ma_device_type_capture) {
33729  status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33730  if (status != noErr) {
33731  if (pDevice->type == ma_device_type_duplex) {
33732  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
33733  }
33734  ma_device__set_state(pDevice, ma_device_state_stopped);
33735  }
33736  }
33737  }
33738 
33739  ma_device__on_notification_rerouted(pDevice);
33740  }
33741  }
33742  }
33743  }
33744  ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
33745 
33746  /* Unused parameters. */
33747  (void)objectID;
33748  (void)pUserData;
33749 
33750  return noErr;
33751 }
33752 
33753 static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
33754 {
33755  MA_ASSERT(pContext != NULL);
33756 
33757  ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
33758  {
33759  /* Don't do anything if we've already initializd device tracking. */
33760  if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
33761  AudioObjectPropertyAddress propAddress;
33762  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
33763  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33764 
33765  ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio);
33766 
33767  propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
33768  ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
33769 
33770  propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
33771  ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
33772 
33773  }
33774  g_DeviceTrackingInitCounter_CoreAudio += 1;
33775  }
33776  ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
33777 
33778  return MA_SUCCESS;
33779 }
33780 
33781 static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
33782 {
33783  MA_ASSERT(pContext != NULL);
33784 
33785  ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
33786  {
33787  if (g_DeviceTrackingInitCounter_CoreAudio > 0)
33788  g_DeviceTrackingInitCounter_CoreAudio -= 1;
33789 
33790  if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
33791  AudioObjectPropertyAddress propAddress;
33792  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
33793  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33794 
33795  propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
33796  ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
33797 
33798  propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
33799  ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
33800 
33801  /* At this point there should be no tracked devices. If not there's an error somewhere. */
33802  if (g_ppTrackedDevices_CoreAudio != NULL) {
33803  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active.");
33804  ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
33805  return MA_INVALID_OPERATION;
33806  }
33807 
33808  ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
33809  }
33810  }
33811  ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
33812 
33813  return MA_SUCCESS;
33814 }
33815 
33816 static ma_result ma_device__track__coreaudio(ma_device* pDevice)
33817 {
33818  MA_ASSERT(pDevice != NULL);
33819 
33820  ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
33821  {
33822  /* Allocate memory if required. */
33823  if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
33824  ma_uint32 newCap;
33825  ma_device** ppNewDevices;
33826 
33827  newCap = g_TrackedDeviceCap_CoreAudio * 2;
33828  if (newCap == 0) {
33829  newCap = 1;
33830  }
33831 
33832  ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks);
33833  if (ppNewDevices == NULL) {
33834  ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
33835  return MA_OUT_OF_MEMORY;
33836  }
33837 
33838  g_ppTrackedDevices_CoreAudio = ppNewDevices;
33839  g_TrackedDeviceCap_CoreAudio = newCap;
33840  }
33841 
33842  g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
33843  g_TrackedDeviceCount_CoreAudio += 1;
33844  }
33845  ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
33846 
33847  return MA_SUCCESS;
33848 }
33849 
33850 static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
33851 {
33852  MA_ASSERT(pDevice != NULL);
33853 
33854  ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
33855  {
33856  ma_uint32 iDevice;
33857  for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
33858  if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
33859  /* We've found the device. We now need to remove it from the list. */
33860  ma_uint32 jDevice;
33861  for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
33862  g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
33863  }
33864 
33865  g_TrackedDeviceCount_CoreAudio -= 1;
33866 
33867  /* If there's nothing else in the list we need to free memory. */
33868  if (g_TrackedDeviceCount_CoreAudio == 0) {
33869  ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
33870  g_ppTrackedDevices_CoreAudio = NULL;
33871  g_TrackedDeviceCap_CoreAudio = 0;
33872  }
33873 
33874  break;
33875  }
33876  }
33877  }
33878  ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
33879 
33880  return MA_SUCCESS;
33881 }
33882 #endif
33883 
33884 #if defined(MA_APPLE_MOBILE)
33885 @interface ma_ios_notification_handler:NSObject {
33886  ma_device* m_pDevice;
33887 }
33888 @end
33889 
33890 @implementation ma_ios_notification_handler
33891 -(id)init:(ma_device*)pDevice
33892 {
33893  self = [super init];
33894  m_pDevice = pDevice;
33895 
33896  /* For route changes. */
33897  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
33898 
33899  /* For interruptions. */
33900  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
33901 
33902  return self;
33903 }
33904 
33905 -(void)dealloc
33906 {
33907  [self remove_handler];
33908 
33909  #if defined(__has_feature)
33910  #if !__has_feature(objc_arc)
33911  [super dealloc];
33912  #endif
33913  #endif
33914 }
33915 
33916 -(void)remove_handler
33917 {
33918  [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
33919  [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
33920 }
33921 
33922 -(void)handle_interruption:(NSNotification*)pNotification
33923 {
33924  NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
33925  switch (type)
33926  {
33927  case AVAudioSessionInterruptionTypeBegan:
33928  {
33929  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n");
33930 
33931  /*
33932  Core Audio will have stopped the internal device automatically, but we need explicitly
33933  stop it at a higher level to ensure miniaudio-specific state is updated for consistency.
33934  */
33935  ma_device_stop(m_pDevice);
33936 
33937  /*
33938  Fire the notification after the device has been stopped to ensure it's in the correct
33939  state when the notification handler is invoked.
33940  */
33941  ma_device__on_notification_interruption_began(m_pDevice);
33942  } break;
33943 
33944  case AVAudioSessionInterruptionTypeEnded:
33945  {
33946  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n");
33947  ma_device__on_notification_interruption_ended(m_pDevice);
33948  } break;
33949  }
33950 }
33951 
33952 -(void)handle_route_change:(NSNotification*)pNotification
33953 {
33954  AVAudioSession* pSession = [AVAudioSession sharedInstance];
33955 
33956  NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
33957  switch (reason)
33958  {
33959  case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
33960  {
33961  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
33962  } break;
33963 
33964  case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
33965  {
33966  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
33967  } break;
33968 
33969  case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
33970  {
33971  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
33972  } break;
33973 
33974  case AVAudioSessionRouteChangeReasonWakeFromSleep:
33975  {
33976  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
33977  } break;
33978 
33979  case AVAudioSessionRouteChangeReasonOverride:
33980  {
33981  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
33982  } break;
33983 
33984  case AVAudioSessionRouteChangeReasonCategoryChange:
33985  {
33986  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
33987  } break;
33988 
33989  case AVAudioSessionRouteChangeReasonUnknown:
33990  default:
33991  {
33992  ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
33993  } break;
33994  }
33995 
33996  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);
33997 
33998  /* Let the application know about the route change. */
33999  ma_device__on_notification_rerouted(m_pDevice);
34000 }
34001 @end
34002 #endif
34003 
34004 static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
34005 {
34006  MA_ASSERT(pDevice != NULL);
34007  MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized);
34008 
34009 #if defined(MA_APPLE_DESKTOP)
34010  /*
34011  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
34012  just gracefully ignore it.
34013  */
34014  ma_device__untrack__coreaudio(pDevice);
34015 #endif
34016 #if defined(MA_APPLE_MOBILE)
34017  if (pDevice->coreaudio.pNotificationHandler != NULL) {
34018  ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler;
34019  [pNotificationHandler remove_handler];
34020  }
34021 #endif
34022 
34023  if (pDevice->coreaudio.audioUnitCapture != NULL) {
34024  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34025  }
34026  if (pDevice->coreaudio.audioUnitPlayback != NULL) {
34027  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34028  }
34029 
34030  if (pDevice->coreaudio.pAudioBufferList) {
34031  ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
34032  }
34033 
34034  return MA_SUCCESS;
34035 }
34036 
34037 typedef struct
34038 {
34039  ma_bool32 allowNominalSampleRateChange;
34040 
34041  /* Input. */
34042  ma_format formatIn;
34043  ma_uint32 channelsIn;
34044  ma_uint32 sampleRateIn;
34045  ma_channel channelMapIn[MA_MAX_CHANNELS];
34046  ma_uint32 periodSizeInFramesIn;
34047  ma_uint32 periodSizeInMillisecondsIn;
34048  ma_uint32 periodsIn;
34049  ma_share_mode shareMode;
34050  ma_performance_profile performanceProfile;
34051  ma_bool32 registerStopEvent;
34052 
34053  /* Output. */
34054 #if defined(MA_APPLE_DESKTOP)
34055  AudioObjectID deviceObjectID;
34056 #endif
34057  AudioComponent component;
34058  AudioUnit audioUnit;
34059  AudioBufferList* pAudioBufferList; /* Only used for input devices. */
34060  ma_format formatOut;
34061  ma_uint32 channelsOut;
34062  ma_uint32 sampleRateOut;
34063  ma_channel channelMapOut[MA_MAX_CHANNELS];
34064  ma_uint32 periodSizeInFramesOut;
34065  ma_uint32 periodsOut;
34066  char deviceName[256];
34067 } ma_device_init_internal_data__coreaudio;
34068 
34069 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. */
34070 {
34071  ma_result result;
34072  OSStatus status;
34073  UInt32 enableIOFlag;
34074  AudioStreamBasicDescription bestFormat;
34075  UInt32 actualPeriodSizeInFrames;
34076  AURenderCallbackStruct callbackInfo;
34077 #if defined(MA_APPLE_DESKTOP)
34078  AudioObjectID deviceObjectID;
34079 #endif
34080 
34081  /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
34082  if (deviceType == ma_device_type_duplex) {
34083  return MA_INVALID_ARGS;
34084  }
34085 
34086  MA_ASSERT(pContext != NULL);
34087  MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
34088 
34089 #if defined(MA_APPLE_DESKTOP)
34090  pData->deviceObjectID = 0;
34091 #endif
34092  pData->component = NULL;
34093  pData->audioUnit = NULL;
34094  pData->pAudioBufferList = NULL;
34095 
34096 #if defined(MA_APPLE_DESKTOP)
34097  result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
34098  if (result != MA_SUCCESS) {
34099  return result;
34100  }
34101 
34102  pData->deviceObjectID = deviceObjectID;
34103 #endif
34104 
34105  /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
34106  pData->periodsOut = pData->periodsIn;
34107  if (pData->periodsOut == 0) {
34108  pData->periodsOut = MA_DEFAULT_PERIODS;
34109  }
34110  if (pData->periodsOut > 16) {
34111  pData->periodsOut = 16;
34112  }
34113 
34114 
34115  /* Audio unit. */
34116  status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
34117  if (status != noErr) {
34118  return ma_result_from_OSStatus(status);
34119  }
34120 
34121 
34122  /* 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. */
34123  enableIOFlag = 1;
34124  if (deviceType == ma_device_type_capture) {
34125  enableIOFlag = 0;
34126  }
34127 
34128  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
34129  if (status != noErr) {
34130  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34131  return ma_result_from_OSStatus(status);
34132  }
34133 
34134  enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
34135  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
34136  if (status != noErr) {
34137  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34138  return ma_result_from_OSStatus(status);
34139  }
34140 
34141 
34142  /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
34143 #if defined(MA_APPLE_DESKTOP)
34144  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
34145  if (status != noErr) {
34146  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34147  return ma_result_from_OSStatus(result);
34148  }
34149 #else
34150  /*
34151  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
34152  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.
34153  */
34154  if (pDeviceID != NULL) {
34155  if (deviceType == ma_device_type_capture) {
34156  ma_bool32 found = MA_FALSE;
34157  NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
34158  for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
34159  if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
34160  [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
34161  found = MA_TRUE;
34162  break;
34163  }
34164  }
34165 
34166  if (found == MA_FALSE) {
34167  return MA_DOES_NOT_EXIST;
34168  }
34169  }
34170  }
34171 #endif
34172 
34173  /*
34174  Format. This is the hardest part of initialization because there's a few variables to take into account.
34175  1) The format must be supported by the device.
34176  2) The format must be supported miniaudio.
34177  3) There's a priority that miniaudio prefers.
34178 
34179  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
34180  most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
34181  for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
34182 
34183  On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
34184  */
34185  {
34186  AudioStreamBasicDescription origFormat;
34187  UInt32 origFormatSize = sizeof(origFormat);
34188  AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
34189  AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
34190 
34191  if (deviceType == ma_device_type_playback) {
34192  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
34193  } else {
34194  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
34195  }
34196  if (status != noErr) {
34197  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34198  return ma_result_from_OSStatus(status);
34199  }
34200 
34201  #if defined(MA_APPLE_DESKTOP)
34202  result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
34203  if (result != MA_SUCCESS) {
34204  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34205  return result;
34206  }
34207 
34208  /*
34209  Technical Note TN2091: Device input using the HAL Output Audio Unit
34210  https://developer.apple.com/library/archive/technotes/tn2091/_index.html
34211 
34212  This documentation says the following:
34213 
34214  The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
34215  variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
34216  conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
34217  another AudioConverter.
34218 
34219  The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
34220  therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
34221  safe and apply the same rule to output as well.
34222 
34223  I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
34224  returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
34225  this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
34226 
34227  Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with
34228  this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
34229  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
34230  configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
34231  rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run
34232  the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is
34233  changed by miniaudio.
34234  */
34235  if (pData->allowNominalSampleRateChange) {
34236  AudioValueRange sampleRateRange;
34237  AudioObjectPropertyAddress propAddress;
34238 
34239  sampleRateRange.mMinimum = bestFormat.mSampleRate;
34240  sampleRateRange.mMaximum = bestFormat.mSampleRate;
34241 
34242  propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
34243  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
34244  propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
34245 
34246  status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
34247  if (status != noErr) {
34248  bestFormat.mSampleRate = origFormat.mSampleRate;
34249  }
34250  } else {
34251  bestFormat.mSampleRate = origFormat.mSampleRate;
34252  }
34253 
34254  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
34255  if (status != noErr) {
34256  /* We failed to set the format, so fall back to the current format of the audio unit. */
34257  bestFormat = origFormat;
34258  }
34259  #else
34260  bestFormat = origFormat;
34261 
34262  /*
34263  Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
34264  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
34265  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
34266  can tell, it looks like the sample rate is shared between playback and capture for everything.
34267  */
34268  @autoreleasepool {
34269  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
34270  MA_ASSERT(pAudioSession != NULL);
34271 
34272  [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
34273  bestFormat.mSampleRate = pAudioSession.sampleRate;
34274 
34275  /*
34276  I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
34277  AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
34278  */
34279  if (deviceType == ma_device_type_playback) {
34280  bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
34281  }
34282  if (deviceType == ma_device_type_capture) {
34283  bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
34284  }
34285  }
34286 
34287  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
34288  if (status != noErr) {
34289  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34290  return ma_result_from_OSStatus(status);
34291  }
34292  #endif
34293 
34294  result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
34295  if (result != MA_SUCCESS) {
34296  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34297  return result;
34298  }
34299 
34300  if (pData->formatOut == ma_format_unknown) {
34301  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34302  return MA_FORMAT_NOT_SUPPORTED;
34303  }
34304 
34305  pData->channelsOut = bestFormat.mChannelsPerFrame;
34306  pData->sampleRateOut = bestFormat.mSampleRate;
34307  }
34308 
34309  /* Clamp the channel count for safety. */
34310  if (pData->channelsOut > MA_MAX_CHANNELS) {
34311  pData->channelsOut = MA_MAX_CHANNELS;
34312  }
34313 
34314  /*
34315  Internal channel map. This is weird in my testing. If I use the AudioObject to get the
34316  channel map, the channel descriptions are set to "Unknown" for some reason. To work around
34317  this it looks like retrieving it from the AudioUnit will work. However, and this is where
34318  it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
34319  I'm going to fall back to a default assumption in these cases.
34320  */
34321 #if defined(MA_APPLE_DESKTOP)
34322  result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
34323  if (result != MA_SUCCESS) {
34324  #if 0
34325  /* Try falling back to the channel map from the AudioObject. */
34326  result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
34327  if (result != MA_SUCCESS) {
34328  return result;
34329  }
34330  #else
34331  /* Fall back to default assumptions. */
34332  ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
34333  #endif
34334  }
34335 #else
34336  /* TODO: Figure out how to get the channel map using AVAudioSession. */
34337  ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
34338 #endif
34339 
34340 
34341  /* Buffer size. Not allowing this to be configurable on iOS. */
34342  if (pData->periodSizeInFramesIn == 0) {
34343  if (pData->periodSizeInMillisecondsIn == 0) {
34344  if (pData->performanceProfile == ma_performance_profile_low_latency) {
34345  actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut);
34346  } else {
34347  actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut);
34348  }
34349  } else {
34350  actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
34351  }
34352  } else {
34353  actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
34354  }
34355 
34356 #if defined(MA_APPLE_DESKTOP)
34357  result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
34358  if (result != MA_SUCCESS) {
34359  return result;
34360  }
34361 #else
34362  /*
34363  On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point
34364  number. I don't trust any potential truncation errors due to converting from float to integer
34365  so I'm going to explicitly set the actual period size to the next power of 2.
34366  */
34367  @autoreleasepool {
34368  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
34369  MA_ASSERT(pAudioSession != NULL);
34370 
34371  [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil];
34372  actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate));
34373  }
34374 #endif
34375 
34376 
34377  /*
34378  During testing I discovered that the buffer size can be too big. You'll get an error like this:
34379 
34380  kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
34381 
34382  Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
34383  of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
34384  */
34385  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
34386  if (status != noErr) {
34387  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34388  return ma_result_from_OSStatus(status);
34389  }
34390 
34391  pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames;
34392 
34393  /* We need a buffer list if this is an input device. We render into this in the input callback. */
34394  if (deviceType == ma_device_type_capture) {
34395  ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
34396  AudioBufferList* pBufferList;
34397 
34398  pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks);
34399  if (pBufferList == NULL) {
34400  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34401  return MA_OUT_OF_MEMORY;
34402  }
34403 
34404  pData->pAudioBufferList = pBufferList;
34405  }
34406 
34407  /* Callbacks. */
34408  callbackInfo.inputProcRefCon = pDevice_DoNotReference;
34409  if (deviceType == ma_device_type_playback) {
34410  callbackInfo.inputProc = ma_on_output__coreaudio;
34411  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
34412  if (status != noErr) {
34413  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34414  return ma_result_from_OSStatus(status);
34415  }
34416  } else {
34417  callbackInfo.inputProc = ma_on_input__coreaudio;
34418  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
34419  if (status != noErr) {
34420  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34421  return ma_result_from_OSStatus(status);
34422  }
34423  }
34424 
34425  /* We need to listen for stop events. */
34426  if (pData->registerStopEvent) {
34427  status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
34428  if (status != noErr) {
34429  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34430  return ma_result_from_OSStatus(status);
34431  }
34432  }
34433 
34434  /* Initialize the audio unit. */
34435  status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
34436  if (status != noErr) {
34437  ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks);
34438  pData->pAudioBufferList = NULL;
34439  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34440  return ma_result_from_OSStatus(status);
34441  }
34442 
34443  /* Grab the name. */
34444 #if defined(MA_APPLE_DESKTOP)
34445  ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
34446 #else
34447  if (deviceType == ma_device_type_playback) {
34448  ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
34449  } else {
34450  ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
34451  }
34452 #endif
34453 
34454  return result;
34455 }
34456 
34457 #if defined(MA_APPLE_DESKTOP)
34458 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
34459 {
34460  ma_device_init_internal_data__coreaudio data;
34461  ma_result result;
34462 
34463  /* This should only be called for playback or capture, not duplex. */
34464  if (deviceType == ma_device_type_duplex) {
34465  return MA_INVALID_ARGS;
34466  }
34467 
34468  data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */
34469 
34470  if (deviceType == ma_device_type_capture) {
34471  data.formatIn = pDevice->capture.format;
34472  data.channelsIn = pDevice->capture.channels;
34473  data.sampleRateIn = pDevice->sampleRate;
34474  MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
34475  data.shareMode = pDevice->capture.shareMode;
34476  data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
34477  data.registerStopEvent = MA_TRUE;
34478 
34479  if (disposePreviousAudioUnit) {
34480  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34481  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34482  }
34483  if (pDevice->coreaudio.pAudioBufferList) {
34484  ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
34485  }
34486  } else if (deviceType == ma_device_type_playback) {
34487  data.formatIn = pDevice->playback.format;
34488  data.channelsIn = pDevice->playback.channels;
34489  data.sampleRateIn = pDevice->sampleRate;
34490  MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
34491  data.shareMode = pDevice->playback.shareMode;
34492  data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
34493  data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
34494 
34495  if (disposePreviousAudioUnit) {
34496  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34497  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34498  }
34499  }
34500  data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
34501  data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
34502  data.periodsIn = pDevice->coreaudio.originalPeriods;
34503 
34504  /* Need at least 3 periods for duplex. */
34505  if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
34506  data.periodsIn = 3;
34507  }
34508 
34509  result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
34510  if (result != MA_SUCCESS) {
34511  return result;
34512  }
34513 
34514  if (deviceType == ma_device_type_capture) {
34515  #if defined(MA_APPLE_DESKTOP)
34516  pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
34517  ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
34518  #endif
34519  pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
34520  pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
34521  pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
34522 
34523  pDevice->capture.internalFormat = data.formatOut;
34524  pDevice->capture.internalChannels = data.channelsOut;
34525  pDevice->capture.internalSampleRate = data.sampleRateOut;
34526  MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
34527  pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
34528  pDevice->capture.internalPeriods = data.periodsOut;
34529  } else if (deviceType == ma_device_type_playback) {
34530  #if defined(MA_APPLE_DESKTOP)
34531  pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
34532  ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
34533  #endif
34534  pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
34535 
34536  pDevice->playback.internalFormat = data.formatOut;
34537  pDevice->playback.internalChannels = data.channelsOut;
34538  pDevice->playback.internalSampleRate = data.sampleRateOut;
34539  MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
34540  pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
34541  pDevice->playback.internalPeriods = data.periodsOut;
34542  }
34543 
34544  return MA_SUCCESS;
34545 }
34546 #endif /* MA_APPLE_DESKTOP */
34547 
34548 static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
34549 {
34550  ma_result result;
34551 
34552  MA_ASSERT(pDevice != NULL);
34553  MA_ASSERT(pConfig != NULL);
34554 
34555  if (pConfig->deviceType == ma_device_type_loopback) {
34556  return MA_DEVICE_TYPE_NOT_SUPPORTED;
34557  }
34558 
34559  /* No exclusive mode with the Core Audio backend for now. */
34560  if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) ||
34561  ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {
34562  return MA_SHARE_MODE_NOT_SUPPORTED;
34563  }
34564 
34565  /* Capture needs to be initialized first. */
34566  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
34567  ma_device_init_internal_data__coreaudio data;
34568  data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
34569  data.formatIn = pDescriptorCapture->format;
34570  data.channelsIn = pDescriptorCapture->channels;
34571  data.sampleRateIn = pDescriptorCapture->sampleRate;
34572  MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
34573  data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
34574  data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
34575  data.periodsIn = pDescriptorCapture->periodCount;
34576  data.shareMode = pDescriptorCapture->shareMode;
34577  data.performanceProfile = pConfig->performanceProfile;
34578  data.registerStopEvent = MA_TRUE;
34579 
34580  /* Need at least 3 periods for duplex. */
34581  if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
34582  data.periodsIn = 3;
34583  }
34584 
34585  result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);
34586  if (result != MA_SUCCESS) {
34587  return result;
34588  }
34589 
34590  pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
34591  #if defined(MA_APPLE_DESKTOP)
34592  pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
34593  #endif
34594  pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
34595  pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
34596  pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
34597  pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
34598  pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
34599  pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount;
34600  pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
34601 
34602  pDescriptorCapture->format = data.formatOut;
34603  pDescriptorCapture->channels = data.channelsOut;
34604  pDescriptorCapture->sampleRate = data.sampleRateOut;
34605  MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
34606  pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
34607  pDescriptorCapture->periodCount = data.periodsOut;
34608 
34609  #if defined(MA_APPLE_DESKTOP)
34610  ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
34611 
34612  /*
34613  If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
34614  switch the device in the background.
34615  */
34616  if (pConfig->capture.pDeviceID == NULL) {
34617  ma_device__track__coreaudio(pDevice);
34618  }
34619  #endif
34620  }
34621 
34622  /* Playback. */
34623  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
34624  ma_device_init_internal_data__coreaudio data;
34625  data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
34626  data.formatIn = pDescriptorPlayback->format;
34627  data.channelsIn = pDescriptorPlayback->channels;
34628  data.sampleRateIn = pDescriptorPlayback->sampleRate;
34629  MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
34630  data.shareMode = pDescriptorPlayback->shareMode;
34631  data.performanceProfile = pConfig->performanceProfile;
34632 
34633  /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
34634  if (pConfig->deviceType == ma_device_type_duplex) {
34635  data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
34636  data.periodsIn = pDescriptorCapture->periodCount;
34637  data.registerStopEvent = MA_FALSE;
34638  } else {
34639  data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
34640  data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
34641  data.periodsIn = pDescriptorPlayback->periodCount;
34642  data.registerStopEvent = MA_TRUE;
34643  }
34644 
34645  result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
34646  if (result != MA_SUCCESS) {
34647  if (pConfig->deviceType == ma_device_type_duplex) {
34648  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34649  if (pDevice->coreaudio.pAudioBufferList) {
34650  ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
34651  }
34652  }
34653  return result;
34654  }
34655 
34656  pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
34657  #if defined(MA_APPLE_DESKTOP)
34658  pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
34659  #endif
34660  pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
34661  pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
34662  pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
34663  pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount;
34664  pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
34665 
34666  pDescriptorPlayback->format = data.formatOut;
34667  pDescriptorPlayback->channels = data.channelsOut;
34668  pDescriptorPlayback->sampleRate = data.sampleRateOut;
34669  MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
34670  pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
34671  pDescriptorPlayback->periodCount = data.periodsOut;
34672 
34673  #if defined(MA_APPLE_DESKTOP)
34674  ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
34675 
34676  /*
34677  If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
34678  switch the device in the background.
34679  */
34680  if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
34681  ma_device__track__coreaudio(pDevice);
34682  }
34683  #endif
34684  }
34685 
34686 
34687 
34688  /*
34689  When stopping the device, a callback is called on another thread. We need to wait for this callback
34690  before returning from ma_device_stop(). This event is used for this.
34691  */
34692  ma_event_init(&pDevice->coreaudio.stopEvent);
34693 
34694  /*
34695  We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
34696  differently on non-Desktop Apple platforms.
34697  */
34698 #if defined(MA_APPLE_MOBILE)
34699  pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];
34700 #endif
34701 
34702  return MA_SUCCESS;
34703 }
34704 
34705 
34706 static ma_result ma_device_start__coreaudio(ma_device* pDevice)
34707 {
34708  MA_ASSERT(pDevice != NULL);
34709 
34710  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
34711  OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34712  if (status != noErr) {
34713  return ma_result_from_OSStatus(status);
34714  }
34715  }
34716 
34717  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
34718  OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34719  if (status != noErr) {
34720  if (pDevice->type == ma_device_type_duplex) {
34721  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34722  }
34723  return ma_result_from_OSStatus(status);
34724  }
34725  }
34726 
34727  return MA_SUCCESS;
34728 }
34729 
34730 static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
34731 {
34732  MA_ASSERT(pDevice != NULL);
34733 
34734  /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
34735 
34736  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
34737  OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34738  if (status != noErr) {
34739  return ma_result_from_OSStatus(status);
34740  }
34741  }
34742 
34743  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
34744  OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34745  if (status != noErr) {
34746  return ma_result_from_OSStatus(status);
34747  }
34748  }
34749 
34750  /* We need to wait for the callback to finish before returning. */
34751  ma_event_wait(&pDevice->coreaudio.stopEvent);
34752  return MA_SUCCESS;
34753 }
34754 
34755 
34756 static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
34757 {
34758  MA_ASSERT(pContext != NULL);
34759  MA_ASSERT(pContext->backend == ma_backend_coreaudio);
34760 
34761 #if defined(MA_APPLE_MOBILE)
34762  if (!pContext->coreaudio.noAudioSessionDeactivate) {
34763  if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {
34764  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.");
34765  return MA_FAILED_TO_INIT_BACKEND;
34766  }
34767  }
34768 #endif
34769 
34770 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
34771  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
34772  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
34773  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
34774 #endif
34775 
34776 #if !defined(MA_APPLE_MOBILE)
34777  ma_context__uninit_device_tracking__coreaudio(pContext);
34778 #endif
34779 
34780  (void)pContext;
34781  return MA_SUCCESS;
34782 }
34783 
34784 #if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0)
34785 static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
34786 {
34787  /* The "default" and "none" categories are treated different and should not be used as an input into this function. */
34788  MA_ASSERT(category != ma_ios_session_category_default);
34789  MA_ASSERT(category != ma_ios_session_category_none);
34790 
34791  switch (category) {
34792  case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
34793  case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
34794  case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
34795  case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
34796  case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
34797  case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
34798  case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
34799  case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
34800  default: return AVAudioSessionCategoryAmbient;
34801  }
34802 }
34803 #endif
34804 
34805 static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
34806 {
34807 #if !defined(MA_APPLE_MOBILE)
34808  ma_result result;
34809 #endif
34810 
34811  MA_ASSERT(pConfig != NULL);
34812  MA_ASSERT(pContext != NULL);
34813 
34814 #if defined(MA_APPLE_MOBILE)
34815  @autoreleasepool {
34816  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
34817  AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
34818 
34819  MA_ASSERT(pAudioSession != NULL);
34820 
34821  if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {
34822  /*
34823  I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
34824  we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
34825  */
34826  #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
34827  options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
34828  #endif
34829 
34830  if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
34831  /* Using PlayAndRecord */
34832  } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
34833  /* Using Playback */
34834  } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
34835  /* Using Record */
34836  } else {
34837  /* Leave as default? */
34838  }
34839  } else {
34840  if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {
34841  #if defined(__IPHONE_12_0)
34842  if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
34843  return MA_INVALID_OPERATION; /* Failed to set session category. */
34844  }
34845  #else
34846  /* Ignore the session category on version 11 and older, but post a warning. */
34847  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer.");
34848  #endif
34849  }
34850  }
34851 
34852  if (!pConfig->coreaudio.noAudioSessionActivate) {
34853  if (![pAudioSession setActive:true error:nil]) {
34854  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session.");
34855  return MA_FAILED_TO_INIT_BACKEND;
34856  }
34857  }
34858  }
34859 #endif
34860 
34861 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
34862  pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
34863  if (pContext->coreaudio.hCoreFoundation == NULL) {
34864  return MA_API_NOT_FOUND;
34865  }
34866 
34867  pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
34868  pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease");
34869 
34870 
34871  pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio");
34872  if (pContext->coreaudio.hCoreAudio == NULL) {
34873  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
34874  return MA_API_NOT_FOUND;
34875  }
34876 
34877  pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
34878  pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
34879  pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
34880  pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
34881  pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
34882 
34883  /*
34884  It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
34885  defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
34886  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
34887  AudioToolbox.
34888  */
34889  pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit");
34890  if (pContext->coreaudio.hAudioUnit == NULL) {
34891  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
34892  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
34893  return MA_API_NOT_FOUND;
34894  }
34895 
34896  if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
34897  /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
34898  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
34899  pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox");
34900  if (pContext->coreaudio.hAudioUnit == NULL) {
34901  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
34902  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
34903  return MA_API_NOT_FOUND;
34904  }
34905  }
34906 
34907  pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
34908  pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
34909  pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
34910  pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
34911  pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
34912  pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
34913  pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
34914  pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
34915  pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
34916  pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
34917  pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender");
34918 #else
34919  pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
34920  pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
34921 
34922  #if defined(MA_APPLE_DESKTOP)
34923  pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
34924  pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
34925  pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
34926  pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
34927  pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
34928  #endif
34929 
34930  pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
34931  pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
34932  pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
34933  pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
34934  pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
34935  pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
34936  pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
34937  pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
34938  pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
34939  pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
34940  pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
34941 #endif
34942 
34943  /* Audio component. */
34944  {
34945  AudioComponentDescription desc;
34946  desc.componentType = kAudioUnitType_Output;
34947  #if defined(MA_APPLE_DESKTOP)
34948  desc.componentSubType = kAudioUnitSubType_HALOutput;
34949  #else
34950  desc.componentSubType = kAudioUnitSubType_RemoteIO;
34951  #endif
34952  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
34953  desc.componentFlags = 0;
34954  desc.componentFlagsMask = 0;
34955 
34956  pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
34957  if (pContext->coreaudio.component == NULL) {
34958  #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
34959  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
34960  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
34961  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
34962  #endif
34963  return MA_FAILED_TO_INIT_BACKEND;
34964  }
34965  }
34966 
34967 #if !defined(MA_APPLE_MOBILE)
34968  result = ma_context__init_device_tracking__coreaudio(pContext);
34969  if (result != MA_SUCCESS) {
34970  #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
34971  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
34972  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
34973  ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
34974  #endif
34975  return result;
34976  }
34977 #endif
34978 
34979  pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
34980 
34981  pCallbacks->onContextInit = ma_context_init__coreaudio;
34982  pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
34983  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;
34984  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio;
34985  pCallbacks->onDeviceInit = ma_device_init__coreaudio;
34986  pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio;
34987  pCallbacks->onDeviceStart = ma_device_start__coreaudio;
34988  pCallbacks->onDeviceStop = ma_device_stop__coreaudio;
34989  pCallbacks->onDeviceRead = NULL;
34990  pCallbacks->onDeviceWrite = NULL;
34991  pCallbacks->onDeviceDataLoop = NULL;
34992 
34993  return MA_SUCCESS;
34994 }
34995 #endif /* Core Audio */
34996 
34997 
34998 
34999 /******************************************************************************
35000 
35001 sndio Backend
35002 
35003 ******************************************************************************/
35004 #ifdef MA_HAS_SNDIO
35005 #include <fcntl.h>
35006 
35007 /*
35008 Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
35009 to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
35010 just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
35011 demand for it or if I can get it tested and debugged more thoroughly.
35012 */
35013 #if 0
35014 #if defined(__NetBSD__) || defined(__OpenBSD__)
35015 #include <sys/audioio.h>
35016 #endif
35017 #if defined(__FreeBSD__) || defined(__DragonFly__)
35018 #include <sys/soundcard.h>
35019 #endif
35020 #endif
35021 
35022 #define MA_SIO_DEVANY "default"
35023 #define MA_SIO_PLAY 1
35024 #define MA_SIO_REC 2
35025 #define MA_SIO_NENC 8
35026 #define MA_SIO_NCHAN 8
35027 #define MA_SIO_NRATE 16
35028 #define MA_SIO_NCONF 4
35029 
35030 struct ma_sio_hdl; /* <-- Opaque */
35031 
35032 struct ma_sio_par
35033 {
35034  unsigned int bits;
35035  unsigned int bps;
35036  unsigned int sig;
35037  unsigned int le;
35038  unsigned int msb;
35039  unsigned int rchan;
35040  unsigned int pchan;
35041  unsigned int rate;
35042  unsigned int bufsz;
35043  unsigned int xrun;
35044  unsigned int round;
35045  unsigned int appbufsz;
35046  int __pad[3];
35047  unsigned int __magic;
35048 };
35049 
35050 struct ma_sio_enc
35051 {
35052  unsigned int bits;
35053  unsigned int bps;
35054  unsigned int sig;
35055  unsigned int le;
35056  unsigned int msb;
35057 };
35058 
35059 struct ma_sio_conf
35060 {
35061  unsigned int enc;
35062  unsigned int rchan;
35063  unsigned int pchan;
35064  unsigned int rate;
35065 };
35066 
35067 struct ma_sio_cap
35068 {
35069  struct ma_sio_enc enc[MA_SIO_NENC];
35070  unsigned int rchan[MA_SIO_NCHAN];
35071  unsigned int pchan[MA_SIO_NCHAN];
35072  unsigned int rate[MA_SIO_NRATE];
35073  int __pad[7];
35074  unsigned int nconf;
35075  struct ma_sio_conf confs[MA_SIO_NCONF];
35076 };
35077 
35078 typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
35079 typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
35080 typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
35081 typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
35082 typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
35083 typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
35084 typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
35085 typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
35086 typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
35087 typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
35088 
35089 static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */
35090 {
35091  ma_uint32 i;
35092  for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
35093  if (g_maStandardSampleRatePriorities[i] == sampleRate) {
35094  return i;
35095  }
35096  }
35097 
35098  return (ma_uint32)-1;
35099 }
35100 
35101 static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
35102 {
35103  /* We only support native-endian right now. */
35104  if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
35105  return ma_format_unknown;
35106  }
35107 
35108  if (bits == 8 && bps == 1 && sig == 0) {
35109  return ma_format_u8;
35110  }
35111  if (bits == 16 && bps == 2 && sig == 1) {
35112  return ma_format_s16;
35113  }
35114  if (bits == 24 && bps == 3 && sig == 1) {
35115  return ma_format_s24;
35116  }
35117  if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
35118  /*return ma_format_s24_32;*/
35119  }
35120  if (bits == 32 && bps == 4 && sig == 1) {
35121  return ma_format_s32;
35122  }
35123 
35124  return ma_format_unknown;
35125 }
35126 
35127 static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
35128 {
35129  ma_format bestFormat;
35130  unsigned int iConfig;
35131 
35132  MA_ASSERT(caps != NULL);
35133 
35134  bestFormat = ma_format_unknown;
35135  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
35136  unsigned int iEncoding;
35137  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
35138  unsigned int bits;
35139  unsigned int bps;
35140  unsigned int sig;
35141  unsigned int le;
35142  unsigned int msb;
35143  ma_format format;
35144 
35145  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
35146  continue;
35147  }
35148 
35149  bits = caps->enc[iEncoding].bits;
35150  bps = caps->enc[iEncoding].bps;
35151  sig = caps->enc[iEncoding].sig;
35152  le = caps->enc[iEncoding].le;
35153  msb = caps->enc[iEncoding].msb;
35154  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
35155  if (format == ma_format_unknown) {
35156  continue; /* Format not supported. */
35157  }
35158 
35159  if (bestFormat == ma_format_unknown) {
35160  bestFormat = format;
35161  } else {
35162  if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
35163  bestFormat = format;
35164  }
35165  }
35166  }
35167  }
35168 
35169  return bestFormat;
35170 }
35171 
35172 static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
35173 {
35174  ma_uint32 maxChannels;
35175  unsigned int iConfig;
35176 
35177  MA_ASSERT(caps != NULL);
35178  MA_ASSERT(requiredFormat != ma_format_unknown);
35179 
35180  /* Just pick whatever configuration has the most channels. */
35181  maxChannels = 0;
35182  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
35183  /* The encoding should be of requiredFormat. */
35184  unsigned int iEncoding;
35185  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
35186  unsigned int iChannel;
35187  unsigned int bits;
35188  unsigned int bps;
35189  unsigned int sig;
35190  unsigned int le;
35191  unsigned int msb;
35192  ma_format format;
35193 
35194  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
35195  continue;
35196  }
35197 
35198  bits = caps->enc[iEncoding].bits;
35199  bps = caps->enc[iEncoding].bps;
35200  sig = caps->enc[iEncoding].sig;
35201  le = caps->enc[iEncoding].le;
35202  msb = caps->enc[iEncoding].msb;
35203  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
35204  if (format != requiredFormat) {
35205  continue;
35206  }
35207 
35208  /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
35209  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
35210  unsigned int chan = 0;
35211  unsigned int channels;
35212 
35213  if (deviceType == ma_device_type_playback) {
35214  chan = caps->confs[iConfig].pchan;
35215  } else {
35216  chan = caps->confs[iConfig].rchan;
35217  }
35218 
35219  if ((chan & (1UL << iChannel)) == 0) {
35220  continue;
35221  }
35222 
35223  if (deviceType == ma_device_type_playback) {
35224  channels = caps->pchan[iChannel];
35225  } else {
35226  channels = caps->rchan[iChannel];
35227  }
35228 
35229  if (maxChannels < channels) {
35230  maxChannels = channels;
35231  }
35232  }
35233  }
35234  }
35235 
35236  return maxChannels;
35237 }
35238 
35239 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)
35240 {
35241  ma_uint32 firstSampleRate;
35242  ma_uint32 bestSampleRate;
35243  unsigned int iConfig;
35244 
35245  MA_ASSERT(caps != NULL);
35246  MA_ASSERT(requiredFormat != ma_format_unknown);
35247  MA_ASSERT(requiredChannels > 0);
35248  MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
35249 
35250  firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
35251  bestSampleRate = 0;
35252 
35253  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
35254  /* The encoding should be of requiredFormat. */
35255  unsigned int iEncoding;
35256  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
35257  unsigned int iChannel;
35258  unsigned int bits;
35259  unsigned int bps;
35260  unsigned int sig;
35261  unsigned int le;
35262  unsigned int msb;
35263  ma_format format;
35264 
35265  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
35266  continue;
35267  }
35268 
35269  bits = caps->enc[iEncoding].bits;
35270  bps = caps->enc[iEncoding].bps;
35271  sig = caps->enc[iEncoding].sig;
35272  le = caps->enc[iEncoding].le;
35273  msb = caps->enc[iEncoding].msb;
35274  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
35275  if (format != requiredFormat) {
35276  continue;
35277  }
35278 
35279  /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
35280  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
35281  unsigned int chan = 0;
35282  unsigned int channels;
35283  unsigned int iRate;
35284 
35285  if (deviceType == ma_device_type_playback) {
35286  chan = caps->confs[iConfig].pchan;
35287  } else {
35288  chan = caps->confs[iConfig].rchan;
35289  }
35290 
35291  if ((chan & (1UL << iChannel)) == 0) {
35292  continue;
35293  }
35294 
35295  if (deviceType == ma_device_type_playback) {
35296  channels = caps->pchan[iChannel];
35297  } else {
35298  channels = caps->rchan[iChannel];
35299  }
35300 
35301  if (channels != requiredChannels) {
35302  continue;
35303  }
35304 
35305  /* Getting here means we have found a compatible encoding/channel pair. */
35306  for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
35307  ma_uint32 rate = (ma_uint32)caps->rate[iRate];
35308  ma_uint32 ratePriority;
35309 
35310  if (firstSampleRate == 0) {
35311  firstSampleRate = rate;
35312  }
35313 
35314  /* Disregard this rate if it's not a standard one. */
35315  ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
35316  if (ratePriority == (ma_uint32)-1) {
35317  continue;
35318  }
35319 
35320  if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */
35321  bestSampleRate = rate;
35322  }
35323  }
35324  }
35325  }
35326  }
35327 
35328  /* If a standard sample rate was not found just fall back to the first one that was iterated. */
35329  if (bestSampleRate == 0) {
35330  bestSampleRate = firstSampleRate;
35331  }
35332 
35333  return bestSampleRate;
35334 }
35335 
35336 
35337 static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
35338 {
35339  ma_bool32 isTerminating = MA_FALSE;
35340  struct ma_sio_hdl* handle;
35341 
35342  MA_ASSERT(pContext != NULL);
35343  MA_ASSERT(callback != NULL);
35344 
35345  /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
35346 
35347  /* Playback. */
35348  if (!isTerminating) {
35349  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
35350  if (handle != NULL) {
35351  /* Supports playback. */
35352  ma_device_info deviceInfo;
35353  MA_ZERO_OBJECT(&deviceInfo);
35354  ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
35355  ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
35356 
35357  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
35358 
35359  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
35360  }
35361  }
35362 
35363  /* Capture. */
35364  if (!isTerminating) {
35365  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
35366  if (handle != NULL) {
35367  /* Supports capture. */
35368  ma_device_info deviceInfo;
35369  MA_ZERO_OBJECT(&deviceInfo);
35370  ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
35371  ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
35372 
35373  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
35374 
35375  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
35376  }
35377  }
35378 
35379  return MA_SUCCESS;
35380 }
35381 
35382 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)
35383 {
35384  char devid[256];
35385  struct ma_sio_hdl* handle;
35386  struct ma_sio_cap caps;
35387  unsigned int iConfig;
35388 
35389  MA_ASSERT(pContext != NULL);
35390 
35391  /* We need to open the device before we can get information about it. */
35392  if (pDeviceID == NULL) {
35393  ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
35394  ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
35395  } else {
35396  ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
35397  ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
35398  }
35399 
35400  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
35401  if (handle == NULL) {
35402  return MA_NO_DEVICE;
35403  }
35404 
35405  if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
35406  return MA_ERROR;
35407  }
35408 
35409  pDeviceInfo->nativeDataFormatCount = 0;
35410 
35411  for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
35412  /*
35413  The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
35414  preference to some formats over others.
35415  */
35416  unsigned int iEncoding;
35417  unsigned int iChannel;
35418  unsigned int iRate;
35419 
35420  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
35421  unsigned int bits;
35422  unsigned int bps;
35423  unsigned int sig;
35424  unsigned int le;
35425  unsigned int msb;
35426  ma_format format;
35427 
35428  if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
35429  continue;
35430  }
35431 
35432  bits = caps.enc[iEncoding].bits;
35433  bps = caps.enc[iEncoding].bps;
35434  sig = caps.enc[iEncoding].sig;
35435  le = caps.enc[iEncoding].le;
35436  msb = caps.enc[iEncoding].msb;
35437  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
35438  if (format == ma_format_unknown) {
35439  continue; /* Format not supported. */
35440  }
35441 
35442 
35443  /* Channels. */
35444  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
35445  unsigned int chan = 0;
35446  unsigned int channels;
35447 
35448  if (deviceType == ma_device_type_playback) {
35449  chan = caps.confs[iConfig].pchan;
35450  } else {
35451  chan = caps.confs[iConfig].rchan;
35452  }
35453 
35454  if ((chan & (1UL << iChannel)) == 0) {
35455  continue;
35456  }
35457 
35458  if (deviceType == ma_device_type_playback) {
35459  channels = caps.pchan[iChannel];
35460  } else {
35461  channels = caps.rchan[iChannel];
35462  }
35463 
35464 
35465  /* Sample Rates. */
35466  for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
35467  if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
35468  ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0);
35469  }
35470  }
35471  }
35472  }
35473  }
35474 
35475  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
35476  return MA_SUCCESS;
35477 }
35478 
35479 static ma_result ma_device_uninit__sndio(ma_device* pDevice)
35480 {
35481  MA_ASSERT(pDevice != NULL);
35482 
35483  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35484  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
35485  }
35486 
35487  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35488  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
35489  }
35490 
35491  return MA_SUCCESS;
35492 }
35493 
35494 static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
35495 {
35496  const char* pDeviceName;
35497  ma_ptr handle;
35498  int openFlags = 0;
35499  struct ma_sio_cap caps;
35500  struct ma_sio_par par;
35501  const ma_device_id* pDeviceID;
35502  ma_format format;
35503  ma_uint32 channels;
35504  ma_uint32 sampleRate;
35505  ma_format internalFormat;
35506  ma_uint32 internalChannels;
35507  ma_uint32 internalSampleRate;
35508  ma_uint32 internalPeriodSizeInFrames;
35509  ma_uint32 internalPeriods;
35510 
35511  MA_ASSERT(pConfig != NULL);
35512  MA_ASSERT(deviceType != ma_device_type_duplex);
35513  MA_ASSERT(pDevice != NULL);
35514 
35515  if (deviceType == ma_device_type_capture) {
35516  openFlags = MA_SIO_REC;
35517  } else {
35518  openFlags = MA_SIO_PLAY;
35519  }
35520 
35521  pDeviceID = pDescriptor->pDeviceID;
35522  format = pDescriptor->format;
35523  channels = pDescriptor->channels;
35524  sampleRate = pDescriptor->sampleRate;
35525 
35526  pDeviceName = MA_SIO_DEVANY;
35527  if (pDeviceID != NULL) {
35528  pDeviceName = pDeviceID->sndio;
35529  }
35530 
35531  handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
35532  if (handle == NULL) {
35533  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.");
35534  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
35535  }
35536 
35537  /* We need to retrieve the device caps to determine the most appropriate format to use. */
35538  if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
35539  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
35540  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.");
35541  return MA_ERROR;
35542  }
35543 
35544  /*
35545  Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
35546  way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
35547  to the requested channels, regardless of whether or not the default channel count is requested.
35548 
35549  For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
35550  value returned by ma_find_best_channels_from_sio_cap__sndio().
35551  */
35552  if (deviceType == ma_device_type_capture) {
35553  if (format == ma_format_unknown) {
35554  format = ma_find_best_format_from_sio_cap__sndio(&caps);
35555  }
35556 
35557  if (channels == 0) {
35558  if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
35559  channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
35560  } else {
35561  channels = MA_DEFAULT_CHANNELS;
35562  }
35563  }
35564  } else {
35565  if (format == ma_format_unknown) {
35566  format = ma_find_best_format_from_sio_cap__sndio(&caps);
35567  }
35568 
35569  if (channels == 0) {
35570  if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
35571  channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
35572  } else {
35573  channels = MA_DEFAULT_CHANNELS;
35574  }
35575  }
35576  }
35577 
35578  if (sampleRate == 0) {
35579  sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
35580  }
35581 
35582 
35583  ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
35584  par.msb = 0;
35585  par.le = ma_is_little_endian();
35586 
35587  switch (format) {
35588  case ma_format_u8:
35589  {
35590  par.bits = 8;
35591  par.bps = 1;
35592  par.sig = 0;
35593  } break;
35594 
35595  case ma_format_s24:
35596  {
35597  par.bits = 24;
35598  par.bps = 3;
35599  par.sig = 1;
35600  } break;
35601 
35602  case ma_format_s32:
35603  {
35604  par.bits = 32;
35605  par.bps = 4;
35606  par.sig = 1;
35607  } break;
35608 
35609  case ma_format_s16:
35610  case ma_format_f32:
35611  case ma_format_unknown:
35612  default:
35613  {
35614  par.bits = 16;
35615  par.bps = 2;
35616  par.sig = 1;
35617  } break;
35618  }
35619 
35620  if (deviceType == ma_device_type_capture) {
35621  par.rchan = channels;
35622  } else {
35623  par.pchan = channels;
35624  }
35625 
35626  par.rate = sampleRate;
35627 
35628  internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile);
35629 
35630  par.round = internalPeriodSizeInFrames;
35631  par.appbufsz = par.round * pDescriptor->periodCount;
35632 
35633  if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
35634  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
35635  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.");
35636  return MA_ERROR;
35637  }
35638 
35639  if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
35640  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
35641  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.");
35642  return MA_ERROR;
35643  }
35644 
35645  internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
35646  internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
35647  internalSampleRate = par.rate;
35648  internalPeriods = par.appbufsz / par.round;
35649  internalPeriodSizeInFrames = par.round;
35650 
35651  if (deviceType == ma_device_type_capture) {
35652  pDevice->sndio.handleCapture = handle;
35653  } else {
35654  pDevice->sndio.handlePlayback = handle;
35655  }
35656 
35657  pDescriptor->format = internalFormat;
35658  pDescriptor->channels = internalChannels;
35659  pDescriptor->sampleRate = internalSampleRate;
35660  ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
35661  pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
35662  pDescriptor->periodCount = internalPeriods;
35663 
35664  return MA_SUCCESS;
35665 }
35666 
35667 static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
35668 {
35669  MA_ASSERT(pDevice != NULL);
35670 
35671  MA_ZERO_OBJECT(&pDevice->sndio);
35672 
35673  if (pConfig->deviceType == ma_device_type_loopback) {
35674  return MA_DEVICE_TYPE_NOT_SUPPORTED;
35675  }
35676 
35677  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
35678  ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
35679  if (result != MA_SUCCESS) {
35680  return result;
35681  }
35682  }
35683 
35684  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
35685  ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
35686  if (result != MA_SUCCESS) {
35687  return result;
35688  }
35689  }
35690 
35691  return MA_SUCCESS;
35692 }
35693 
35694 static ma_result ma_device_start__sndio(ma_device* pDevice)
35695 {
35696  MA_ASSERT(pDevice != NULL);
35697 
35698  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35699  ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
35700  }
35701 
35702  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
35703  ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
35704  }
35705 
35706  return MA_SUCCESS;
35707 }
35708 
35709 static ma_result ma_device_stop__sndio(ma_device* pDevice)
35710 {
35711  MA_ASSERT(pDevice != NULL);
35712 
35713  /*
35714  From the documentation:
35715 
35716  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
35717  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
35718  buffer is drained. In no case are samples in the play buffer discarded.
35719 
35720  Therefore, sio_stop() performs all of the necessary draining for us.
35721  */
35722 
35723  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35724  ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
35725  }
35726 
35727  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
35728  ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
35729  }
35730 
35731  return MA_SUCCESS;
35732 }
35733 
35734 static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
35735 {
35736  int result;
35737 
35738  if (pFramesWritten != NULL) {
35739  *pFramesWritten = 0;
35740  }
35741 
35742  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));
35743  if (result == 0) {
35744  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.");
35745  return MA_IO_ERROR;
35746  }
35747 
35748  if (pFramesWritten != NULL) {
35749  *pFramesWritten = frameCount;
35750  }
35751 
35752  return MA_SUCCESS;
35753 }
35754 
35755 static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
35756 {
35757  int result;
35758 
35759  if (pFramesRead != NULL) {
35760  *pFramesRead = 0;
35761  }
35762 
35763  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));
35764  if (result == 0) {
35765  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.");
35766  return MA_IO_ERROR;
35767  }
35768 
35769  if (pFramesRead != NULL) {
35770  *pFramesRead = frameCount;
35771  }
35772 
35773  return MA_SUCCESS;
35774 }
35775 
35776 static ma_result ma_context_uninit__sndio(ma_context* pContext)
35777 {
35778  MA_ASSERT(pContext != NULL);
35779  MA_ASSERT(pContext->backend == ma_backend_sndio);
35780 
35781  (void)pContext;
35782  return MA_SUCCESS;
35783 }
35784 
35785 static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
35786 {
35787 #ifndef MA_NO_RUNTIME_LINKING
35788  const char* libsndioNames[] = {
35789  "libsndio.so"
35790  };
35791  size_t i;
35792 
35793  for (i = 0; i < ma_countof(libsndioNames); ++i) {
35794  pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]);
35795  if (pContext->sndio.sndioSO != NULL) {
35796  break;
35797  }
35798  }
35799 
35800  if (pContext->sndio.sndioSO == NULL) {
35801  return MA_NO_BACKEND;
35802  }
35803 
35804  pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open");
35805  pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close");
35806  pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar");
35807  pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar");
35808  pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap");
35809  pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write");
35810  pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read");
35811  pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start");
35812  pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop");
35813  pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar");
35814 #else
35815  pContext->sndio.sio_open = sio_open;
35816  pContext->sndio.sio_close = sio_close;
35817  pContext->sndio.sio_setpar = sio_setpar;
35818  pContext->sndio.sio_getpar = sio_getpar;
35819  pContext->sndio.sio_getcap = sio_getcap;
35820  pContext->sndio.sio_write = sio_write;
35821  pContext->sndio.sio_read = sio_read;
35822  pContext->sndio.sio_start = sio_start;
35823  pContext->sndio.sio_stop = sio_stop;
35824  pContext->sndio.sio_initpar = sio_initpar;
35825 #endif
35826 
35827  pCallbacks->onContextInit = ma_context_init__sndio;
35828  pCallbacks->onContextUninit = ma_context_uninit__sndio;
35829  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio;
35830  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio;
35831  pCallbacks->onDeviceInit = ma_device_init__sndio;
35832  pCallbacks->onDeviceUninit = ma_device_uninit__sndio;
35833  pCallbacks->onDeviceStart = ma_device_start__sndio;
35834  pCallbacks->onDeviceStop = ma_device_stop__sndio;
35835  pCallbacks->onDeviceRead = ma_device_read__sndio;
35836  pCallbacks->onDeviceWrite = ma_device_write__sndio;
35837  pCallbacks->onDeviceDataLoop = NULL;
35838 
35839  (void)pConfig;
35840  return MA_SUCCESS;
35841 }
35842 #endif /* sndio */
35843 
35844 
35845 
35846 /******************************************************************************
35847 
35848 audio(4) Backend
35849 
35850 ******************************************************************************/
35851 #ifdef MA_HAS_AUDIO4
35852 #include <fcntl.h>
35853 #include <poll.h>
35854 #include <errno.h>
35855 #include <sys/stat.h>
35856 #include <sys/types.h>
35857 #include <sys/ioctl.h>
35858 #include <sys/audioio.h>
35859 
35860 #if defined(__OpenBSD__)
35861  #include <sys/param.h>
35862  #if defined(OpenBSD) && OpenBSD >= 201709
35863  #define MA_AUDIO4_USE_NEW_API
35864  #endif
35865 #endif
35866 
35867 static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
35868 {
35869  size_t baseLen;
35870 
35871  MA_ASSERT(id != NULL);
35872  MA_ASSERT(idSize > 0);
35873  MA_ASSERT(deviceIndex >= 0);
35874 
35875  baseLen = strlen(base);
35876  MA_ASSERT(idSize > baseLen);
35877 
35878  ma_strcpy_s(id, idSize, base);
35879  ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
35880 }
35881 
35882 static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
35883 {
35884  size_t idLen;
35885  size_t baseLen;
35886  const char* deviceIndexStr;
35887 
35888  MA_ASSERT(id != NULL);
35889  MA_ASSERT(base != NULL);
35890  MA_ASSERT(pIndexOut != NULL);
35891 
35892  idLen = strlen(id);
35893  baseLen = strlen(base);
35894  if (idLen <= baseLen) {
35895  return MA_ERROR; /* Doesn't look like the id starts with the base. */
35896  }
35897 
35898  if (strncmp(id, base, baseLen) != 0) {
35899  return MA_ERROR; /* ID does not begin with base. */
35900  }
35901 
35902  deviceIndexStr = id + baseLen;
35903  if (deviceIndexStr[0] == '\0') {
35904  return MA_ERROR; /* No index specified in the ID. */
35905  }
35906 
35907  if (pIndexOut) {
35908  *pIndexOut = atoi(deviceIndexStr);
35909  }
35910 
35911  return MA_SUCCESS;
35912 }
35913 
35914 
35915 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
35916 static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
35917 {
35918  if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
35919  return ma_format_u8;
35920  } else {
35921  if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
35922  if (precision == 16) {
35923  return ma_format_s16;
35924  } else if (precision == 24) {
35925  return ma_format_s24;
35926  } else if (precision == 32) {
35927  return ma_format_s32;
35928  }
35929  } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
35930  if (precision == 16) {
35931  return ma_format_s16;
35932  } else if (precision == 24) {
35933  return ma_format_s24;
35934  } else if (precision == 32) {
35935  return ma_format_s32;
35936  }
35937  }
35938  }
35939 
35940  return ma_format_unknown; /* Encoding not supported. */
35941 }
35942 
35943 static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
35944 {
35945  MA_ASSERT(pEncoding != NULL);
35946  MA_ASSERT(pPrecision != NULL);
35947 
35948  switch (format)
35949  {
35950  case ma_format_u8:
35951  {
35952  *pEncoding = AUDIO_ENCODING_ULINEAR;
35953  *pPrecision = 8;
35954  } break;
35955 
35956  case ma_format_s24:
35957  {
35958  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
35959  *pPrecision = 24;
35960  } break;
35961 
35962  case ma_format_s32:
35963  {
35964  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
35965  *pPrecision = 32;
35966  } break;
35967 
35968  case ma_format_s16:
35969  case ma_format_f32:
35970  case ma_format_unknown:
35971  default:
35972  {
35973  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
35974  *pPrecision = 16;
35975  } break;
35976  }
35977 }
35978 
35979 static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
35980 {
35981  return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
35982 }
35983 
35984 static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat)
35985 {
35986  audio_encoding_t encoding;
35987  ma_uint32 iFormat;
35988  int counter = 0;
35989 
35990  /* First check to see if the preferred format is supported. */
35991  if (preferredFormat != ma_format_unknown) {
35992  counter = 0;
35993  for (;;) {
35994  MA_ZERO_OBJECT(&encoding);
35995  encoding.index = counter;
35996  if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
35997  break;
35998  }
35999 
36000  if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
36001  return preferredFormat; /* Found the preferred format. */
36002  }
36003 
36004  /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
36005  counter += 1;
36006  }
36007  }
36008 
36009  /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */
36010  for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
36011  ma_format format = g_maFormatPriorities[iFormat];
36012 
36013  counter = 0;
36014  for (;;) {
36015  MA_ZERO_OBJECT(&encoding);
36016  encoding.index = counter;
36017  if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
36018  break;
36019  }
36020 
36021  if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
36022  return format; /* Found a workable format. */
36023  }
36024 
36025  /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
36026  counter += 1;
36027  }
36028  }
36029 
36030  /* Getting here means not appropriate format was found. */
36031  return ma_format_unknown;
36032 }
36033 #else
36034 static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
36035 {
36036  if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
36037  return ma_format_u8;
36038  }
36039  if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
36040  return ma_format_s16;
36041  }
36042  if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
36043  return ma_format_s24;
36044  }
36045  if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
36046  return ma_format_f32;
36047  }
36048 
36049  /* Format not supported. */
36050  return ma_format_unknown;
36051 }
36052 #endif
36053 
36054 static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo)
36055 {
36056  audio_device_t fdDevice;
36057 
36058  MA_ASSERT(pContext != NULL);
36059  MA_ASSERT(fd >= 0);
36060  MA_ASSERT(pDeviceInfo != NULL);
36061 
36062  (void)pContext;
36063  (void)deviceType;
36064 
36065  if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
36066  return MA_ERROR; /* Failed to retrieve device info. */
36067  }
36068 
36069  /* Name. */
36070  ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name);
36071 
36072  #if !defined(MA_AUDIO4_USE_NEW_API)
36073  {
36074  audio_info_t fdInfo;
36075  int counter = 0;
36076  ma_uint32 channels;
36077  ma_uint32 sampleRate;
36078 
36079  if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
36080  return MA_ERROR;
36081  }
36082 
36083  if (deviceType == ma_device_type_playback) {
36084  channels = fdInfo.play.channels;
36085  sampleRate = fdInfo.play.sample_rate;
36086  } else {
36087  channels = fdInfo.record.channels;
36088  sampleRate = fdInfo.record.sample_rate;
36089  }
36090 
36091  /* Supported formats. We get this by looking at the encodings. */
36092  pDeviceInfo->nativeDataFormatCount = 0;
36093  for (;;) {
36094  audio_encoding_t encoding;
36095  ma_format format;
36096 
36097  MA_ZERO_OBJECT(&encoding);
36098  encoding.index = counter;
36099  if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
36100  break;
36101  }
36102 
36103  format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
36104  if (format != ma_format_unknown) {
36105  ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
36106  }
36107 
36108  counter += 1;
36109  }
36110  }
36111  #else
36112  {
36113  struct audio_swpar fdPar;
36114  ma_format format;
36115  ma_uint32 channels;
36116  ma_uint32 sampleRate;
36117 
36118  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
36119  return MA_ERROR;
36120  }
36121 
36122  format = ma_format_from_swpar__audio4(&fdPar);
36123  if (format == ma_format_unknown) {
36124  return MA_FORMAT_NOT_SUPPORTED;
36125  }
36126 
36127  if (deviceType == ma_device_type_playback) {
36128  channels = fdPar.pchan;
36129  } else {
36130  channels = fdPar.rchan;
36131  }
36132 
36133  sampleRate = fdPar.rate;
36134 
36135  pDeviceInfo->nativeDataFormatCount = 0;
36136  ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
36137  }
36138  #endif
36139 
36140  return MA_SUCCESS;
36141 }
36142 
36143 static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
36144 {
36145  const int maxDevices = 64;
36146  char devpath[256];
36147  int iDevice;
36148 
36149  MA_ASSERT(pContext != NULL);
36150  MA_ASSERT(callback != NULL);
36151 
36152  /*
36153  Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
36154  version here since we can open it even when another process has control of the "/dev/audioN" device.
36155  */
36156  for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
36157  struct stat st;
36158  int fd;
36159  ma_bool32 isTerminating = MA_FALSE;
36160 
36161  ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
36162  ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
36163 
36164  if (stat(devpath, &st) < 0) {
36165  break;
36166  }
36167 
36168  /* The device exists, but we need to check if it's usable as playback and/or capture. */
36169 
36170  /* Playback. */
36171  if (!isTerminating) {
36172  fd = open(devpath, O_RDONLY, 0);
36173  if (fd >= 0) {
36174  /* Supports playback. */
36175  ma_device_info deviceInfo;
36176  MA_ZERO_OBJECT(&deviceInfo);
36177  ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
36178  if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
36179  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
36180  }
36181 
36182  close(fd);
36183  }
36184  }
36185 
36186  /* Capture. */
36187  if (!isTerminating) {
36188  fd = open(devpath, O_WRONLY, 0);
36189  if (fd >= 0) {
36190  /* Supports capture. */
36191  ma_device_info deviceInfo;
36192  MA_ZERO_OBJECT(&deviceInfo);
36193  ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
36194  if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
36195  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
36196  }
36197 
36198  close(fd);
36199  }
36200  }
36201 
36202  if (isTerminating) {
36203  break;
36204  }
36205  }
36206 
36207  return MA_SUCCESS;
36208 }
36209 
36210 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)
36211 {
36212  int fd = -1;
36213  int deviceIndex = -1;
36214  char ctlid[256];
36215  ma_result result;
36216 
36217  MA_ASSERT(pContext != NULL);
36218 
36219  /*
36220  We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
36221  from the device ID which will be in "/dev/audioN" format.
36222  */
36223  if (pDeviceID == NULL) {
36224  /* Default device. */
36225  ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
36226  } else {
36227  /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
36228  result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
36229  if (result != MA_SUCCESS) {
36230  return result;
36231  }
36232 
36233  ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
36234  }
36235 
36236  fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
36237  if (fd == -1) {
36238  return MA_NO_DEVICE;
36239  }
36240 
36241  if (deviceIndex == -1) {
36242  ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
36243  } else {
36244  ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
36245  }
36246 
36247  result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
36248 
36249  close(fd);
36250  return result;
36251 }
36252 
36253 static ma_result ma_device_uninit__audio4(ma_device* pDevice)
36254 {
36255  MA_ASSERT(pDevice != NULL);
36256 
36257  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36258  close(pDevice->audio4.fdCapture);
36259  }
36260 
36261  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36262  close(pDevice->audio4.fdPlayback);
36263  }
36264 
36265  return MA_SUCCESS;
36266 }
36267 
36268 static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
36269 {
36270  const char* pDefaultDeviceNames[] = {
36271  "/dev/audio",
36272  "/dev/audio0"
36273  };
36274  const char* pDefaultDeviceCtlNames[] = {
36275  "/dev/audioctl",
36276  "/dev/audioctl0"
36277  };
36278  int fd;
36279  int fdFlags = 0;
36280  size_t iDefaultDevice = (size_t)-1;
36281  ma_format internalFormat;
36282  ma_uint32 internalChannels;
36283  ma_uint32 internalSampleRate;
36284  ma_uint32 internalPeriodSizeInFrames;
36285  ma_uint32 internalPeriods;
36286 
36287  MA_ASSERT(pConfig != NULL);
36288  MA_ASSERT(deviceType != ma_device_type_duplex);
36289  MA_ASSERT(pDevice != NULL);
36290 
36291  /* The first thing to do is open the file. */
36292  if (deviceType == ma_device_type_capture) {
36293  fdFlags = O_RDONLY;
36294  } else {
36295  fdFlags = O_WRONLY;
36296  }
36297  /*fdFlags |= O_NONBLOCK;*/
36298 
36299  /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */
36300  if (pDescriptor->pDeviceID == NULL) {
36301  /* Default device. */
36302  for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) {
36303  fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0);
36304  if (fd != -1) {
36305  break;
36306  }
36307  }
36308  } else {
36309  /* Specific device. */
36310  fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0);
36311 
36312  for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) {
36313  if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) {
36314  break;
36315  }
36316  }
36317 
36318  if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) {
36319  iDefaultDevice = (size_t)-1;
36320  }
36321  }
36322 
36323  if (fd == -1) {
36324  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.");
36325  return ma_result_from_errno(errno);
36326  }
36327 
36328  #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
36329  {
36330  audio_info_t fdInfo;
36331  int fdInfoResult = -1;
36332 
36333  /*
36334  The documentation is a little bit unclear to me as to how it handles formats. It says the
36335  following:
36336 
36337  Regardless of formats supported by underlying driver, the audio driver accepts the
36338  following formats.
36339 
36340  By then the next sentence says this:
36341 
36342  `encoding` and `precision` are one of the values obtained by AUDIO_GETENC.
36343 
36344  It sounds like a direct contradiction to me. I'm going to play this safe any only use the
36345  best sample format returned by AUDIO_GETENC. If the requested format is supported we'll
36346  use that, but otherwise we'll just use our standard format priorities to pick an
36347  appropriate one.
36348  */
36349  AUDIO_INITINFO(&fdInfo);
36350 
36351  /*
36352  Get the default format from the audioctl file if we're asking for a default device. If we
36353  retrieve it from /dev/audio it'll default to mono 8000Hz.
36354  */
36355  if (iDefaultDevice != (size_t)-1) {
36356  /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */
36357  int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);
36358  if (fdctl != -1) {
36359  fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);
36360  close(fdctl);
36361  }
36362  }
36363 
36364  if (fdInfoResult == -1) {
36365  /* We still don't have the default device info so just retrieve it from the main audio device. */
36366  if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
36367  close(fd);
36368  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
36369  return ma_result_from_errno(errno);
36370  }
36371  }
36372 
36373  /* We get the driver to do as much of the data conversion as possible. */
36374  if (deviceType == ma_device_type_capture) {
36375  fdInfo.mode = AUMODE_RECORD;
36376  ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision);
36377 
36378  if (pDescriptor->channels != 0) {
36379  fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
36380  }
36381 
36382  if (pDescriptor->sampleRate != 0) {
36383  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`.) */
36384  }
36385  } else {
36386  fdInfo.mode = AUMODE_PLAY;
36387  ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision);
36388 
36389  if (pDescriptor->channels != 0) {
36390  fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
36391  }
36392 
36393  if (pDescriptor->sampleRate != 0) {
36394  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`.) */
36395  }
36396  }
36397 
36398  if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
36399  close(fd);
36400  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.");
36401  return ma_result_from_errno(errno);
36402  }
36403 
36404  if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
36405  close(fd);
36406  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
36407  return ma_result_from_errno(errno);
36408  }
36409 
36410  if (deviceType == ma_device_type_capture) {
36411  internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
36412  internalChannels = fdInfo.record.channels;
36413  internalSampleRate = fdInfo.record.sample_rate;
36414  } else {
36415  internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
36416  internalChannels = fdInfo.play.channels;
36417  internalSampleRate = fdInfo.play.sample_rate;
36418  }
36419 
36420  if (internalFormat == ma_format_unknown) {
36421  close(fd);
36422  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.");
36423  return MA_FORMAT_NOT_SUPPORTED;
36424  }
36425 
36426  /* Buffer. */
36427  {
36428  ma_uint32 internalPeriodSizeInBytes;
36429 
36430  internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
36431 
36432  internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
36433  if (internalPeriodSizeInBytes < 16) {
36434  internalPeriodSizeInBytes = 16;
36435  }
36436 
36437  internalPeriods = pDescriptor->periodCount;
36438  if (internalPeriods < 2) {
36439  internalPeriods = 2;
36440  }
36441 
36442  /* What miniaudio calls a period, audio4 calls a block. */
36443  AUDIO_INITINFO(&fdInfo);
36444  fdInfo.hiwat = internalPeriods;
36445  fdInfo.lowat = internalPeriods-1;
36446  fdInfo.blocksize = internalPeriodSizeInBytes;
36447  if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
36448  close(fd);
36449  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.");
36450  return ma_result_from_errno(errno);
36451  }
36452 
36453  internalPeriods = fdInfo.hiwat;
36454  internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
36455  }
36456  }
36457  #else
36458  {
36459  struct audio_swpar fdPar;
36460 
36461  /* 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. */
36462  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
36463  close(fd);
36464  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.");
36465  return ma_result_from_errno(errno);
36466  }
36467 
36468  internalFormat = ma_format_from_swpar__audio4(&fdPar);
36469  internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
36470  internalSampleRate = fdPar.rate;
36471 
36472  if (internalFormat == ma_format_unknown) {
36473  close(fd);
36474  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.");
36475  return MA_FORMAT_NOT_SUPPORTED;
36476  }
36477 
36478  /* Buffer. */
36479  {
36480  ma_uint32 internalPeriodSizeInBytes;
36481 
36482  internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
36483 
36484  /* What miniaudio calls a period, audio4 calls a block. */
36485  internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
36486  if (internalPeriodSizeInBytes < 16) {
36487  internalPeriodSizeInBytes = 16;
36488  }
36489 
36490  fdPar.nblks = pDescriptor->periodCount;
36491  fdPar.round = internalPeriodSizeInBytes;
36492 
36493  if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
36494  close(fd);
36495  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.");
36496  return ma_result_from_errno(errno);
36497  }
36498 
36499  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
36500  close(fd);
36501  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.");
36502  return ma_result_from_errno(errno);
36503  }
36504  }
36505 
36506  internalFormat = ma_format_from_swpar__audio4(&fdPar);
36507  internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
36508  internalSampleRate = fdPar.rate;
36509  internalPeriods = fdPar.nblks;
36510  internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
36511  }
36512  #endif
36513 
36514  if (internalFormat == ma_format_unknown) {
36515  close(fd);
36516  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.");
36517  return MA_FORMAT_NOT_SUPPORTED;
36518  }
36519 
36520  if (deviceType == ma_device_type_capture) {
36521  pDevice->audio4.fdCapture = fd;
36522  } else {
36523  pDevice->audio4.fdPlayback = fd;
36524  }
36525 
36526  pDescriptor->format = internalFormat;
36527  pDescriptor->channels = internalChannels;
36528  pDescriptor->sampleRate = internalSampleRate;
36529  ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
36530  pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
36531  pDescriptor->periodCount = internalPeriods;
36532 
36533  return MA_SUCCESS;
36534 }
36535 
36536 static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
36537 {
36538  MA_ASSERT(pDevice != NULL);
36539 
36540  MA_ZERO_OBJECT(&pDevice->audio4);
36541 
36542  if (pConfig->deviceType == ma_device_type_loopback) {
36543  return MA_DEVICE_TYPE_NOT_SUPPORTED;
36544  }
36545 
36546  pDevice->audio4.fdCapture = -1;
36547  pDevice->audio4.fdPlayback = -1;
36548 
36549  /*
36550  The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
36551  introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
36552  I'm aware.
36553  */
36554 #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
36555  /* NetBSD 8.0+ */
36556  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
36557  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
36558  return MA_SHARE_MODE_NOT_SUPPORTED;
36559  }
36560 #else
36561  /* All other flavors. */
36562 #endif
36563 
36564  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
36565  ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
36566  if (result != MA_SUCCESS) {
36567  return result;
36568  }
36569  }
36570 
36571  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
36572  ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
36573  if (result != MA_SUCCESS) {
36574  if (pConfig->deviceType == ma_device_type_duplex) {
36575  close(pDevice->audio4.fdCapture);
36576  }
36577  return result;
36578  }
36579  }
36580 
36581  return MA_SUCCESS;
36582 }
36583 
36584 static ma_result ma_device_start__audio4(ma_device* pDevice)
36585 {
36586  MA_ASSERT(pDevice != NULL);
36587 
36588  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36589  if (pDevice->audio4.fdCapture == -1) {
36590  return MA_INVALID_ARGS;
36591  }
36592  }
36593 
36594  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36595  if (pDevice->audio4.fdPlayback == -1) {
36596  return MA_INVALID_ARGS;
36597  }
36598  }
36599 
36600  return MA_SUCCESS;
36601 }
36602 
36603 static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
36604 {
36605  if (fd == -1) {
36606  return MA_INVALID_ARGS;
36607  }
36608 
36609 #if !defined(MA_AUDIO4_USE_NEW_API)
36610  if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
36611  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.");
36612  return ma_result_from_errno(errno);
36613  }
36614 #else
36615  if (ioctl(fd, AUDIO_STOP, 0) < 0) {
36616  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.");
36617  return ma_result_from_errno(errno);
36618  }
36619 #endif
36620 
36621  return MA_SUCCESS;
36622 }
36623 
36624 static ma_result ma_device_stop__audio4(ma_device* pDevice)
36625 {
36626  MA_ASSERT(pDevice != NULL);
36627 
36628  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36629  ma_result result;
36630 
36631  result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
36632  if (result != MA_SUCCESS) {
36633  return result;
36634  }
36635  }
36636 
36637  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36638  ma_result result;
36639 
36640  /* 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. */
36641  #if !defined(MA_AUDIO4_USE_NEW_API)
36642  ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
36643  #endif
36644 
36645  /* Here is where the device is stopped immediately. */
36646  result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
36647  if (result != MA_SUCCESS) {
36648  return result;
36649  }
36650  }
36651 
36652  return MA_SUCCESS;
36653 }
36654 
36655 static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
36656 {
36657  int result;
36658 
36659  if (pFramesWritten != NULL) {
36660  *pFramesWritten = 0;
36661  }
36662 
36663  result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
36664  if (result < 0) {
36665  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.");
36666  return ma_result_from_errno(errno);
36667  }
36668 
36669  if (pFramesWritten != NULL) {
36670  *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
36671  }
36672 
36673  return MA_SUCCESS;
36674 }
36675 
36676 static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
36677 {
36678  int result;
36679 
36680  if (pFramesRead != NULL) {
36681  *pFramesRead = 0;
36682  }
36683 
36684  result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
36685  if (result < 0) {
36686  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.");
36687  return ma_result_from_errno(errno);
36688  }
36689 
36690  if (pFramesRead != NULL) {
36691  *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
36692  }
36693 
36694  return MA_SUCCESS;
36695 }
36696 
36697 static ma_result ma_context_uninit__audio4(ma_context* pContext)
36698 {
36699  MA_ASSERT(pContext != NULL);
36700  MA_ASSERT(pContext->backend == ma_backend_audio4);
36701 
36702  (void)pContext;
36703  return MA_SUCCESS;
36704 }
36705 
36706 static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
36707 {
36708  MA_ASSERT(pContext != NULL);
36709 
36710  (void)pConfig;
36711 
36712  pCallbacks->onContextInit = ma_context_init__audio4;
36713  pCallbacks->onContextUninit = ma_context_uninit__audio4;
36714  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4;
36715  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4;
36716  pCallbacks->onDeviceInit = ma_device_init__audio4;
36717  pCallbacks->onDeviceUninit = ma_device_uninit__audio4;
36718  pCallbacks->onDeviceStart = ma_device_start__audio4;
36719  pCallbacks->onDeviceStop = ma_device_stop__audio4;
36720  pCallbacks->onDeviceRead = ma_device_read__audio4;
36721  pCallbacks->onDeviceWrite = ma_device_write__audio4;
36722  pCallbacks->onDeviceDataLoop = NULL;
36723 
36724  return MA_SUCCESS;
36725 }
36726 #endif /* audio4 */
36727 
36728 
36729 /******************************************************************************
36730 
36731 OSS Backend
36732 
36733 ******************************************************************************/
36734 #ifdef MA_HAS_OSS
36735 #include <sys/ioctl.h>
36736 #include <unistd.h>
36737 #include <fcntl.h>
36738 #include <sys/soundcard.h>
36739 
36740 #ifndef SNDCTL_DSP_HALT
36741 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
36742 #endif
36743 
36744 #define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp"
36745 
36746 static int ma_open_temp_device__oss()
36747 {
36748  /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
36749  int fd = open("/dev/mixer", O_RDONLY, 0);
36750  if (fd >= 0) {
36751  return fd;
36752  }
36753 
36754  return -1;
36755 }
36756 
36757 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)
36758 {
36759  const char* deviceName;
36760  int flags;
36761 
36762  MA_ASSERT(pContext != NULL);
36763  MA_ASSERT(pfd != NULL);
36764  (void)pContext;
36765 
36766  *pfd = -1;
36767 
36768  /* This function should only be called for playback or capture, not duplex. */
36769  if (deviceType == ma_device_type_duplex) {
36770  return MA_INVALID_ARGS;
36771  }
36772 
36773  deviceName = MA_OSS_DEFAULT_DEVICE_NAME;
36774  if (pDeviceID != NULL) {
36775  deviceName = pDeviceID->oss;
36776  }
36777 
36778  flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
36779  if (shareMode == ma_share_mode_exclusive) {
36780  flags |= O_EXCL;
36781  }
36782 
36783  *pfd = open(deviceName, flags, 0);
36784  if (*pfd == -1) {
36785  return ma_result_from_errno(errno);
36786  }
36787 
36788  return MA_SUCCESS;
36789 }
36790 
36791 static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
36792 {
36793  int fd;
36794  oss_sysinfo si;
36795  int result;
36796 
36797  MA_ASSERT(pContext != NULL);
36798  MA_ASSERT(callback != NULL);
36799 
36800  fd = ma_open_temp_device__oss();
36801  if (fd == -1) {
36802  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.");
36803  return MA_NO_BACKEND;
36804  }
36805 
36806  result = ioctl(fd, SNDCTL_SYSINFO, &si);
36807  if (result != -1) {
36808  int iAudioDevice;
36809  for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
36810  oss_audioinfo ai;
36811  ai.dev = iAudioDevice;
36812  result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
36813  if (result != -1) {
36814  if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
36815  ma_device_info deviceInfo;
36816  ma_bool32 isTerminating = MA_FALSE;
36817 
36818  MA_ZERO_OBJECT(&deviceInfo);
36819 
36820  /* ID */
36821  ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
36822 
36823  /*
36824  The human readable device name should be in the "ai.handle" variable, but it can
36825  sometimes be empty in which case we just fall back to "ai.name" which is less user
36826  friendly, but usually has a value.
36827  */
36828  if (ai.handle[0] != '\0') {
36829  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
36830  } else {
36831  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
36832  }
36833 
36834  /* The device can be both playback and capture. */
36835  if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
36836  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
36837  }
36838  if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
36839  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
36840  }
36841 
36842  if (isTerminating) {
36843  break;
36844  }
36845  }
36846  }
36847  }
36848  } else {
36849  close(fd);
36850  ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
36851  return MA_NO_BACKEND;
36852  }
36853 
36854  close(fd);
36855  return MA_SUCCESS;
36856 }
36857 
36858 static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)
36859 {
36860  unsigned int minChannels;
36861  unsigned int maxChannels;
36862  unsigned int iRate;
36863 
36864  MA_ASSERT(pContext != NULL);
36865  MA_ASSERT(pAudioInfo != NULL);
36866  MA_ASSERT(pDeviceInfo != NULL);
36867 
36868  /* If we support all channels we just report 0. */
36869  minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
36870  maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
36871 
36872  /*
36873  OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness,
36874  which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which
36875  case we'll need to use min_rate and max_rate and report only standard rates.
36876  */
36877  if (pAudioInfo->nrates > 0) {
36878  for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {
36879  unsigned int rate = pAudioInfo->rates[iRate];
36880 
36881  if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
36882  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. */
36883  } else {
36884  unsigned int iChannel;
36885  for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
36886  ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);
36887  }
36888  }
36889  }
36890  } else {
36891  for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {
36892  ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];
36893 
36894  if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {
36895  if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
36896  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. */
36897  } else {
36898  unsigned int iChannel;
36899  for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
36900  ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);
36901  }
36902  }
36903  }
36904  }
36905  }
36906 }
36907 
36908 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)
36909 {
36910  ma_bool32 foundDevice;
36911  int fdTemp;
36912  oss_sysinfo si;
36913  int result;
36914 
36915  MA_ASSERT(pContext != NULL);
36916 
36917  /* Handle the default device a little differently. */
36918  if (pDeviceID == NULL) {
36919  if (deviceType == ma_device_type_playback) {
36920  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
36921  } else {
36922  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
36923  }
36924 
36925  return MA_SUCCESS;
36926  }
36927 
36928 
36929  /* If we get here it means we are _not_ using the default device. */
36930  foundDevice = MA_FALSE;
36931 
36932  fdTemp = ma_open_temp_device__oss();
36933  if (fdTemp == -1) {
36934  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.");
36935  return MA_NO_BACKEND;
36936  }
36937 
36938  result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
36939  if (result != -1) {
36940  int iAudioDevice;
36941  for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
36942  oss_audioinfo ai;
36943  ai.dev = iAudioDevice;
36944  result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
36945  if (result != -1) {
36946  if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
36947  /* It has the same name, so now just confirm the type. */
36948  if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
36949  (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
36950  unsigned int formatMask;
36951 
36952  /* ID */
36953  ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
36954 
36955  /*
36956  The human readable device name should be in the "ai.handle" variable, but it can
36957  sometimes be empty in which case we just fall back to "ai.name" which is less user
36958  friendly, but usually has a value.
36959  */
36960  if (ai.handle[0] != '\0') {
36961  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
36962  } else {
36963  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
36964  }
36965 
36966 
36967  pDeviceInfo->nativeDataFormatCount = 0;
36968 
36969  if (deviceType == ma_device_type_playback) {
36970  formatMask = ai.oformats;
36971  } else {
36972  formatMask = ai.iformats;
36973  }
36974 
36975  if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
36976  ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);
36977  }
36978  if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
36979  ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);
36980  }
36981  if ((formatMask & AFMT_U8) != 0) {
36982  ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);
36983  }
36984 
36985  foundDevice = MA_TRUE;
36986  break;
36987  }
36988  }
36989  }
36990  }
36991  } else {
36992  close(fdTemp);
36993  ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
36994  return MA_NO_BACKEND;
36995  }
36996 
36997 
36998  close(fdTemp);
36999 
37000  if (!foundDevice) {
37001  return MA_NO_DEVICE;
37002  }
37003 
37004  return MA_SUCCESS;
37005 }
37006 
37007 static ma_result ma_device_uninit__oss(ma_device* pDevice)
37008 {
37009  MA_ASSERT(pDevice != NULL);
37010 
37011  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37012  close(pDevice->oss.fdCapture);
37013  }
37014 
37015  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37016  close(pDevice->oss.fdPlayback);
37017  }
37018 
37019  return MA_SUCCESS;
37020 }
37021 
37022 static int ma_format_to_oss(ma_format format)
37023 {
37024  int ossFormat = AFMT_U8;
37025  switch (format) {
37026  case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
37027  case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
37028  case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
37029  case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
37030  case ma_format_u8:
37031  default: ossFormat = AFMT_U8; break;
37032  }
37033 
37034  return ossFormat;
37035 }
37036 
37037 static ma_format ma_format_from_oss(int ossFormat)
37038 {
37039  if (ossFormat == AFMT_U8) {
37040  return ma_format_u8;
37041  } else {
37042  if (ma_is_little_endian()) {
37043  switch (ossFormat) {
37044  case AFMT_S16_LE: return ma_format_s16;
37045  case AFMT_S32_LE: return ma_format_s32;
37046  default: return ma_format_unknown;
37047  }
37048  } else {
37049  switch (ossFormat) {
37050  case AFMT_S16_BE: return ma_format_s16;
37051  case AFMT_S32_BE: return ma_format_s32;
37052  default: return ma_format_unknown;
37053  }
37054  }
37055  }
37056 
37057  return ma_format_unknown;
37058 }
37059 
37060 static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
37061 {
37062  ma_result result;
37063  int ossResult;
37064  int fd;
37065  const ma_device_id* pDeviceID = NULL;
37066  ma_share_mode shareMode;
37067  int ossFormat;
37068  int ossChannels;
37069  int ossSampleRate;
37070  int ossFragment;
37071 
37072  MA_ASSERT(pDevice != NULL);
37073  MA_ASSERT(pConfig != NULL);
37074  MA_ASSERT(deviceType != ma_device_type_duplex);
37075 
37076  pDeviceID = pDescriptor->pDeviceID;
37077  shareMode = pDescriptor->shareMode;
37078  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. */
37079  ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
37080  ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
37081 
37082  result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd);
37083  if (result != MA_SUCCESS) {
37084  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
37085  return result;
37086  }
37087 
37088  /*
37089  The OSS documantation is very clear about the order we should be initializing the device's properties:
37090  1) Format
37091  2) Channels
37092  3) Sample rate.
37093  */
37094 
37095  /* Format. */
37096  ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
37097  if (ossResult == -1) {
37098  close(fd);
37099  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.");
37100  return ma_result_from_errno(errno);
37101  }
37102 
37103  /* Channels. */
37104  ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
37105  if (ossResult == -1) {
37106  close(fd);
37107  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.");
37108  return ma_result_from_errno(errno);
37109  }
37110 
37111  /* Sample Rate. */
37112  ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
37113  if (ossResult == -1) {
37114  close(fd);
37115  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.");
37116  return ma_result_from_errno(errno);
37117  }
37118 
37119  /*
37120  Buffer.
37121 
37122  The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
37123  it should be done before or after format/channels/rate.
37124 
37125  OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
37126  value.
37127  */
37128  {
37129  ma_uint32 periodSizeInFrames;
37130  ma_uint32 periodSizeInBytes;
37131  ma_uint32 ossFragmentSizePower;
37132 
37133  periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile);
37134 
37135  periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
37136  if (periodSizeInBytes < 16) {
37137  periodSizeInBytes = 16;
37138  }
37139 
37140  ossFragmentSizePower = 4;
37141  periodSizeInBytes >>= 4;
37142  while (periodSizeInBytes >>= 1) {
37143  ossFragmentSizePower += 1;
37144  }
37145 
37146  ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
37147  ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
37148  if (ossResult == -1) {
37149  close(fd);
37150  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.");
37151  return ma_result_from_errno(errno);
37152  }
37153  }
37154 
37155  /* Internal settings. */
37156  if (deviceType == ma_device_type_capture) {
37157  pDevice->oss.fdCapture = fd;
37158  } else {
37159  pDevice->oss.fdPlayback = fd;
37160  }
37161 
37162  pDescriptor->format = ma_format_from_oss(ossFormat);
37163  pDescriptor->channels = ossChannels;
37164  pDescriptor->sampleRate = ossSampleRate;
37165  ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
37166  pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16);
37167  pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels);
37168 
37169  if (pDescriptor->format == ma_format_unknown) {
37170  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.");
37171  return MA_FORMAT_NOT_SUPPORTED;
37172  }
37173 
37174  return MA_SUCCESS;
37175 }
37176 
37177 static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
37178 {
37179  MA_ASSERT(pDevice != NULL);
37180  MA_ASSERT(pConfig != NULL);
37181 
37182  MA_ZERO_OBJECT(&pDevice->oss);
37183 
37184  if (pConfig->deviceType == ma_device_type_loopback) {
37185  return MA_DEVICE_TYPE_NOT_SUPPORTED;
37186  }
37187 
37188  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
37189  ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
37190  if (result != MA_SUCCESS) {
37191  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
37192  return result;
37193  }
37194  }
37195 
37196  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
37197  ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
37198  if (result != MA_SUCCESS) {
37199  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
37200  return result;
37201  }
37202  }
37203 
37204  return MA_SUCCESS;
37205 }
37206 
37207 /*
37208 Note on Starting and Stopping
37209 =============================
37210 In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when
37211 trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will
37212 fail. Instead what we need to do is just not write or read to and from the device when the
37213 device is not running.
37214 
37215 As a result, both the start and stop functions for OSS are just empty stubs. The starting and
37216 stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check
37217 the device state, and if the device is stopped they will simply not do any kind of processing.
37218 
37219 The downside to this technique is that I've noticed a fairly lengthy delay in stopping the
37220 device, up to a second. This is on a virtual machine, and as such might just be due to the
37221 virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for
37222 the moment that's just how it's going to have to be.
37223 
37224 When starting the device, OSS will automatically start it when write() or read() is called.
37225 */
37226 static ma_result ma_device_start__oss(ma_device* pDevice)
37227 {
37228  MA_ASSERT(pDevice != NULL);
37229 
37230  /* The device is automatically started with reading and writing. */
37231  (void)pDevice;
37232 
37233  return MA_SUCCESS;
37234 }
37235 
37236 static ma_result ma_device_stop__oss(ma_device* pDevice)
37237 {
37238  MA_ASSERT(pDevice != NULL);
37239 
37240  /* See note above on why this is empty. */
37241  (void)pDevice;
37242 
37243  return MA_SUCCESS;
37244 }
37245 
37246 static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
37247 {
37248  int resultOSS;
37249  ma_uint32 deviceState;
37250 
37251  if (pFramesWritten != NULL) {
37252  *pFramesWritten = 0;
37253  }
37254 
37255  /* Don't do any processing if the device is stopped. */
37256  deviceState = ma_device_get_state(pDevice);
37257  if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
37258  return MA_SUCCESS;
37259  }
37260 
37261  resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
37262  if (resultOSS < 0) {
37263  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.");
37264  return ma_result_from_errno(errno);
37265  }
37266 
37267  if (pFramesWritten != NULL) {
37268  *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
37269  }
37270 
37271  return MA_SUCCESS;
37272 }
37273 
37274 static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
37275 {
37276  int resultOSS;
37277  ma_uint32 deviceState;
37278 
37279  if (pFramesRead != NULL) {
37280  *pFramesRead = 0;
37281  }
37282 
37283  /* Don't do any processing if the device is stopped. */
37284  deviceState = ma_device_get_state(pDevice);
37285  if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
37286  return MA_SUCCESS;
37287  }
37288 
37289  resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
37290  if (resultOSS < 0) {
37291  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.");
37292  return ma_result_from_errno(errno);
37293  }
37294 
37295  if (pFramesRead != NULL) {
37296  *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
37297  }
37298 
37299  return MA_SUCCESS;
37300 }
37301 
37302 static ma_result ma_context_uninit__oss(ma_context* pContext)
37303 {
37304  MA_ASSERT(pContext != NULL);
37305  MA_ASSERT(pContext->backend == ma_backend_oss);
37306 
37307  (void)pContext;
37308  return MA_SUCCESS;
37309 }
37310 
37311 static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
37312 {
37313  int fd;
37314  int ossVersion;
37315  int result;
37316 
37317  MA_ASSERT(pContext != NULL);
37318 
37319  (void)pConfig;
37320 
37321  /* Try opening a temporary device first so we can get version information. This is closed at the end. */
37322  fd = ma_open_temp_device__oss();
37323  if (fd == -1) {
37324  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. */
37325  return MA_NO_BACKEND;
37326  }
37327 
37328  /* Grab the OSS version. */
37329  ossVersion = 0;
37330  result = ioctl(fd, OSS_GETVERSION, &ossVersion);
37331  if (result == -1) {
37332  close(fd);
37333  ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.");
37334  return MA_NO_BACKEND;
37335  }
37336 
37337  /* The file handle to temp device is no longer needed. Close ASAP. */
37338  close(fd);
37339 
37340  pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
37341  pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
37342 
37343  pCallbacks->onContextInit = ma_context_init__oss;
37344  pCallbacks->onContextUninit = ma_context_uninit__oss;
37345  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;
37346  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss;
37347  pCallbacks->onDeviceInit = ma_device_init__oss;
37348  pCallbacks->onDeviceUninit = ma_device_uninit__oss;
37349  pCallbacks->onDeviceStart = ma_device_start__oss;
37350  pCallbacks->onDeviceStop = ma_device_stop__oss;
37351  pCallbacks->onDeviceRead = ma_device_read__oss;
37352  pCallbacks->onDeviceWrite = ma_device_write__oss;
37353  pCallbacks->onDeviceDataLoop = NULL;
37354 
37355  return MA_SUCCESS;
37356 }
37357 #endif /* OSS */
37358 
37359 
37360 
37361 
37362 
37363 /******************************************************************************
37364 
37365 AAudio Backend
37366 
37367 ******************************************************************************/
37368 #ifdef MA_HAS_AAUDIO
37369 
37370 /*#include <AAudio/AAudio.h>*/
37371 
37372 typedef int32_t ma_aaudio_result_t;
37373 typedef int32_t ma_aaudio_direction_t;
37374 typedef int32_t ma_aaudio_sharing_mode_t;
37375 typedef int32_t ma_aaudio_format_t;
37376 typedef int32_t ma_aaudio_stream_state_t;
37377 typedef int32_t ma_aaudio_performance_mode_t;
37378 typedef int32_t ma_aaudio_usage_t;
37379 typedef int32_t ma_aaudio_content_type_t;
37380 typedef int32_t ma_aaudio_input_preset_t;
37381 typedef int32_t ma_aaudio_allowed_capture_policy_t;
37382 typedef int32_t ma_aaudio_data_callback_result_t;
37383 typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
37384 typedef struct ma_AAudioStream_t* ma_AAudioStream;
37385 
37386 #define MA_AAUDIO_UNSPECIFIED 0
37387 
37388 /* Result codes. miniaudio only cares about the success code. */
37389 #define MA_AAUDIO_OK 0
37390 
37391 /* Directions. */
37392 #define MA_AAUDIO_DIRECTION_OUTPUT 0
37393 #define MA_AAUDIO_DIRECTION_INPUT 1
37394 
37395 /* Sharing modes. */
37396 #define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
37397 #define MA_AAUDIO_SHARING_MODE_SHARED 1
37398 
37399 /* Formats. */
37400 #define MA_AAUDIO_FORMAT_PCM_I16 1
37401 #define MA_AAUDIO_FORMAT_PCM_FLOAT 2
37402 
37403 /* Stream states. */
37404 #define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
37405 #define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
37406 #define MA_AAUDIO_STREAM_STATE_OPEN 2
37407 #define MA_AAUDIO_STREAM_STATE_STARTING 3
37408 #define MA_AAUDIO_STREAM_STATE_STARTED 4
37409 #define MA_AAUDIO_STREAM_STATE_PAUSING 5
37410 #define MA_AAUDIO_STREAM_STATE_PAUSED 6
37411 #define MA_AAUDIO_STREAM_STATE_FLUSHING 7
37412 #define MA_AAUDIO_STREAM_STATE_FLUSHED 8
37413 #define MA_AAUDIO_STREAM_STATE_STOPPING 9
37414 #define MA_AAUDIO_STREAM_STATE_STOPPED 10
37415 #define MA_AAUDIO_STREAM_STATE_CLOSING 11
37416 #define MA_AAUDIO_STREAM_STATE_CLOSED 12
37417 #define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
37418 
37419 /* Performance modes. */
37420 #define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
37421 #define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
37422 #define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
37423 
37424 /* Usage types. */
37425 #define MA_AAUDIO_USAGE_MEDIA 1
37426 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2
37427 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3
37428 #define MA_AAUDIO_USAGE_ALARM 4
37429 #define MA_AAUDIO_USAGE_NOTIFICATION 5
37430 #define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6
37431 #define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10
37432 #define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11
37433 #define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12
37434 #define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13
37435 #define MA_AAUDIO_USAGE_GAME 14
37436 #define MA_AAUDIO_USAGE_ASSISTANT 16
37437 #define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000
37438 #define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001
37439 #define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002
37440 #define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003
37441 
37442 /* Content types. */
37443 #define MA_AAUDIO_CONTENT_TYPE_SPEECH 1
37444 #define MA_AAUDIO_CONTENT_TYPE_MUSIC 2
37445 #define MA_AAUDIO_CONTENT_TYPE_MOVIE 3
37446 #define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4
37447 
37448 /* Input presets. */
37449 #define MA_AAUDIO_INPUT_PRESET_GENERIC 1
37450 #define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5
37451 #define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6
37452 #define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7
37453 #define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9
37454 #define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10
37455 
37456 /* Allowed Capture Policies */
37457 #define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1
37458 #define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2
37459 #define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3
37460 
37461 /* Callback results. */
37462 #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
37463 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1
37464 
37465 
37466 typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
37467 typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
37468 
37469 typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
37470 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
37471 typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
37472 typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
37473 typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
37474 typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
37475 typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
37476 typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
37477 typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
37478 typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
37479 typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
37480 typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
37481 typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
37482 typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);
37483 typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);
37484 typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);
37485 typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy);
37486 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
37487 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
37488 typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
37489 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);
37490 typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
37491 typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
37492 typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
37493 typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
37494 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
37495 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
37496 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
37497 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
37498 
37499 static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
37500 {
37501  switch (resultAA)
37502  {
37503  case MA_AAUDIO_OK: return MA_SUCCESS;
37504  default: break;
37505  }
37506 
37507  return MA_ERROR;
37508 }
37509 
37510 static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)
37511 {
37512  switch (usage) {
37513  case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA;
37514  case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
37515  case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
37516  case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM;
37517  case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION;
37518  case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
37519  case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
37520  case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
37521  case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
37522  case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
37523  case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME;
37524  case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT;
37525  case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
37526  case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
37527  case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
37528  case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
37529  default: break;
37530  }
37531 
37532  return MA_AAUDIO_USAGE_MEDIA;
37533 }
37534 
37535 static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
37536 {
37537  switch (contentType) {
37538  case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
37539  case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
37540  case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
37541  case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
37542  default: break;
37543  }
37544 
37545  return MA_AAUDIO_CONTENT_TYPE_SPEECH;
37546 }
37547 
37548 static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
37549 {
37550  switch (inputPreset) {
37551  case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
37552  case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
37553  case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
37554  case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
37555  case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
37556  case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
37557  default: break;
37558  }
37559 
37560  return MA_AAUDIO_INPUT_PRESET_GENERIC;
37561 }
37562 
37563 static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy)
37564 {
37565  switch (allowedCapturePolicy) {
37566  case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;
37567  case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM;
37568  case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE;
37569  default: break;
37570  }
37571 
37572  return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;
37573 }
37574 
37575 static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
37576 {
37577  ma_result result;
37578  ma_job job;
37579  ma_device* pDevice = (ma_device*)pUserData;
37580  MA_ASSERT(pDevice != NULL);
37581 
37582  (void)error;
37583 
37584  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));
37585 
37586  /*
37587  When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation,
37588  we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this
37589  cleanly and safely.
37590  */
37591  job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE);
37592  job.data.device.aaudio.reroute.pDevice = pDevice;
37593 
37594  if (pStream == pDevice->aaudio.pStreamCapture) {
37595  job.data.device.aaudio.reroute.deviceType = ma_device_type_capture;
37596  }
37597  else {
37598  job.data.device.aaudio.reroute.deviceType = ma_device_type_playback;
37599  }
37600 
37601  result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job);
37602  if (result != MA_SUCCESS) {
37603  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n");
37604  return;
37605  }
37606 }
37607 
37608 static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
37609 {
37610  ma_device* pDevice = (ma_device*)pUserData;
37611  MA_ASSERT(pDevice != NULL);
37612 
37613  ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);
37614 
37615  (void)pStream;
37616  return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
37617 }
37618 
37619 static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
37620 {
37621  ma_device* pDevice = (ma_device*)pUserData;
37622  MA_ASSERT(pDevice != NULL);
37623 
37624  ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);
37625 
37626  (void)pStream;
37627  return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
37628 }
37629 
37630 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)
37631 {
37632  ma_AAudioStreamBuilder* pBuilder;
37633  ma_aaudio_result_t resultAA;
37634 
37635  /* Safety. */
37636  *ppBuilder = NULL;
37637 
37638  resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
37639  if (resultAA != MA_AAUDIO_OK) {
37640  return ma_result_from_aaudio(resultAA);
37641  }
37642 
37643  if (pDeviceID != NULL) {
37644  ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
37645  }
37646 
37647  ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
37648  ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
37649 
37650 
37651  /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */
37652  if (pDescriptor != NULL) {
37653  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. */
37654 
37655  if (pDescriptor->sampleRate != 0) {
37656  ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
37657  }
37658 
37659  if (deviceType == ma_device_type_capture) {
37660  if (pDescriptor->channels != 0) {
37661  ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
37662  }
37663  if (pDescriptor->format != ma_format_unknown) {
37664  ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
37665  }
37666  } else {
37667  if (pDescriptor->channels != 0) {
37668  ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
37669  }
37670  if (pDescriptor->format != ma_format_unknown) {
37671  ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
37672  }
37673  }
37674 
37675 
37676  /*
37677  There have been reports where setting the frames per data callback results in an error
37678  later on from Android. To address this, I'm experimenting with simply not setting it on
37679  anything from Android 11 and earlier. Suggestions welcome on how we might be able to make
37680  this more targetted.
37681  */
37682  if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) {
37683  /*
37684  AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you
37685  retrieve the actual sample rate until after you've opened the stream. But you need to configure
37686  the buffer capacity before you open the stream... :/
37687 
37688  To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on.
37689  */
37690  ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;
37691 
37692  ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
37693  ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);
37694  }
37695 
37696  if (deviceType == ma_device_type_capture) {
37697  if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {
37698  ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));
37699  }
37700 
37701  ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
37702  } else {
37703  if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {
37704  ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));
37705  }
37706 
37707  if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {
37708  ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));
37709  }
37710 
37711  if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) {
37712  ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy));
37713  }
37714 
37715  ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
37716  }
37717 
37718  /* 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. */
37719  ((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);
37720 
37721  /* We need to set an error callback to detect device changes. */
37722  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. */
37723  ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
37724  }
37725  }
37726 
37727  *ppBuilder = pBuilder;
37728 
37729  return MA_SUCCESS;
37730 }
37731 
37732 static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream)
37733 {
37734  ma_result result;
37735 
37736  result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream));
37737  ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
37738 
37739  return result;
37740 }
37741 
37742 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)
37743 {
37744  ma_result result;
37745  ma_AAudioStreamBuilder* pBuilder;
37746 
37747  *ppStream = NULL;
37748 
37749  result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder);
37750  if (result != MA_SUCCESS) {
37751  return result;
37752  }
37753 
37754  return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);
37755 }
37756 
37757 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)
37758 {
37759  ma_result result;
37760  ma_AAudioStreamBuilder* pBuilder;
37761 
37762  MA_ASSERT(pDevice != NULL);
37763  MA_ASSERT(pDescriptor != NULL);
37764  MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
37765 
37766  *ppStream = NULL;
37767 
37768  result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder);
37769  if (result != MA_SUCCESS) {
37770  return result;
37771  }
37772 
37773  return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream);
37774 }
37775 
37776 static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
37777 {
37778  return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
37779 }
37780 
37781 static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
37782 {
37783  /* The only way to know this is to try creating a stream. */
37784  ma_AAudioStream* pStream;
37785  ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream);
37786  if (result != MA_SUCCESS) {
37787  return MA_FALSE;
37788  }
37789 
37790  ma_close_stream__aaudio(pContext, pStream);
37791  return MA_TRUE;
37792 }
37793 
37794 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)
37795 {
37796  ma_aaudio_stream_state_t actualNewState;
37797  ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
37798  if (resultAA != MA_AAUDIO_OK) {
37799  return ma_result_from_aaudio(resultAA);
37800  }
37801 
37802  if (newState != actualNewState) {
37803  return MA_ERROR; /* Failed to transition into the expected state. */
37804  }
37805 
37806  return MA_SUCCESS;
37807 }
37808 
37809 
37810 static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
37811 {
37812  ma_bool32 cbResult = MA_TRUE;
37813 
37814  MA_ASSERT(pContext != NULL);
37815  MA_ASSERT(callback != NULL);
37816 
37817  /* 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. */
37818 
37819  /* Playback. */
37820  if (cbResult) {
37821  ma_device_info deviceInfo;
37822  MA_ZERO_OBJECT(&deviceInfo);
37823  deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
37824  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
37825 
37826  if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
37827  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
37828  }
37829  }
37830 
37831  /* Capture. */
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_CAPTURE_DEVICE_NAME, (size_t)-1);
37837 
37838  if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
37839  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
37840  }
37841  }
37842 
37843  return MA_SUCCESS;
37844 }
37845 
37846 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)
37847 {
37848  MA_ASSERT(pContext != NULL);
37849  MA_ASSERT(pStream != NULL);
37850  MA_ASSERT(pDeviceInfo != NULL);
37851 
37852  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
37853  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
37854  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
37855  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
37856  pDeviceInfo->nativeDataFormatCount += 1;
37857 }
37858 
37859 static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo)
37860 {
37861  /* AAudio supports s16 and f32. */
37862  ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo);
37863  ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo);
37864 }
37865 
37866 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)
37867 {
37868  ma_AAudioStream* pStream;
37869  ma_result result;
37870 
37871  MA_ASSERT(pContext != NULL);
37872 
37873  /* ID */
37874  if (pDeviceID != NULL) {
37875  pDeviceInfo->id.aaudio = pDeviceID->aaudio;
37876  } else {
37877  pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
37878  }
37879 
37880  /* Name */
37881  if (deviceType == ma_device_type_playback) {
37882  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
37883  } else {
37884  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
37885  }
37886 
37887 
37888  pDeviceInfo->nativeDataFormatCount = 0;
37889 
37890  /* We'll need to open the device to get accurate sample rate and channel count information. */
37891  result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream);
37892  if (result != MA_SUCCESS) {
37893  return result;
37894  }
37895 
37896  ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo);
37897 
37898  ma_close_stream__aaudio(pContext, pStream);
37899  pStream = NULL;
37900 
37901  return MA_SUCCESS;
37902 }
37903 
37904 
37905 static ma_result ma_device_uninit__aaudio(ma_device* pDevice)
37906 {
37907  MA_ASSERT(pDevice != NULL);
37908 
37909  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37910  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
37911  pDevice->aaudio.pStreamCapture = NULL;
37912  }
37913 
37914  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37915  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
37916  pDevice->aaudio.pStreamPlayback = NULL;
37917  }
37918 
37919  return MA_SUCCESS;
37920 }
37921 
37922 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)
37923 {
37924  ma_result result;
37925  int32_t bufferCapacityInFrames;
37926  int32_t framesPerDataCallback;
37927  ma_AAudioStream* pStream;
37928 
37929  MA_ASSERT(pDevice != NULL);
37930  MA_ASSERT(pConfig != NULL);
37931  MA_ASSERT(pDescriptor != NULL);
37932 
37933  *ppStream = NULL; /* Safety. */
37934 
37935  /* First step is to open the stream. From there we'll be able to extract the internal configuration. */
37936  result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream);
37937  if (result != MA_SUCCESS) {
37938  return result; /* Failed to open the AAudio stream. */
37939  }
37940 
37941  /* Now extract the internal configuration. */
37942  pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
37943  pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream);
37944  pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream);
37945 
37946  /* For the channel map we need to be sure we don't overflow any buffers. */
37947  if (pDescriptor->channels <= MA_MAX_CHANNELS) {
37948  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. */
37949  } else {
37950  ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */
37951  }
37952 
37953  bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream);
37954  framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream);
37955 
37956  if (framesPerDataCallback > 0) {
37957  pDescriptor->periodSizeInFrames = framesPerDataCallback;
37958  pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback;
37959  } else {
37960  pDescriptor->periodSizeInFrames = bufferCapacityInFrames;
37961  pDescriptor->periodCount = 1;
37962  }
37963 
37964  *ppStream = pStream;
37965 
37966  return MA_SUCCESS;
37967 }
37968 
37969 static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
37970 {
37971  ma_result result;
37972 
37973  MA_ASSERT(pDevice != NULL);
37974 
37975  if (pConfig->deviceType == ma_device_type_loopback) {
37976  return MA_DEVICE_TYPE_NOT_SUPPORTED;
37977  }
37978 
37979  pDevice->aaudio.usage = pConfig->aaudio.usage;
37980  pDevice->aaudio.contentType = pConfig->aaudio.contentType;
37981  pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset;
37982  pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy;
37983  pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute;
37984 
37985  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
37986  result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
37987  if (result != MA_SUCCESS) {
37988  return result;
37989  }
37990  }
37991 
37992  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
37993  result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
37994  if (result != MA_SUCCESS) {
37995  return result;
37996  }
37997  }
37998 
37999  return MA_SUCCESS;
38000 }
38001 
38002 static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
38003 {
38004  ma_aaudio_result_t resultAA;
38005  ma_aaudio_stream_state_t currentState;
38006 
38007  MA_ASSERT(pDevice != NULL);
38008 
38009  resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
38010  if (resultAA != MA_AAUDIO_OK) {
38011  return ma_result_from_aaudio(resultAA);
38012  }
38013 
38014  /* Do we actually need to wait for the device to transition into it's started state? */
38015 
38016  /* 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. */
38017  currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
38018  if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
38019  ma_result result;
38020 
38021  if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
38022  return MA_ERROR; /* Expecting the stream to be a starting or started state. */
38023  }
38024 
38025  result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
38026  if (result != MA_SUCCESS) {
38027  return result;
38028  }
38029  }
38030 
38031  return MA_SUCCESS;
38032 }
38033 
38034 static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
38035 {
38036  ma_aaudio_result_t resultAA;
38037  ma_aaudio_stream_state_t currentState;
38038 
38039  MA_ASSERT(pDevice != NULL);
38040 
38041  /*
38042  From the AAudio documentation:
38043 
38044  The stream will stop after all of the data currently buffered has been played.
38045 
38046  This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.
38047  */
38048  currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
38049  if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
38050  return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */
38051  }
38052 
38053  resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
38054  if (resultAA != MA_AAUDIO_OK) {
38055  return ma_result_from_aaudio(resultAA);
38056  }
38057 
38058  /* 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. */
38059  currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
38060  if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
38061  ma_result result;
38062 
38063  if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
38064  return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
38065  }
38066 
38067  result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
38068  if (result != MA_SUCCESS) {
38069  return result;
38070  }
38071  }
38072 
38073  return MA_SUCCESS;
38074 }
38075 
38076 static ma_result ma_device_start__aaudio(ma_device* pDevice)
38077 {
38078  MA_ASSERT(pDevice != NULL);
38079 
38080  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38081  ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
38082  if (result != MA_SUCCESS) {
38083  return result;
38084  }
38085  }
38086 
38087  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38088  ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
38089  if (result != MA_SUCCESS) {
38090  if (pDevice->type == ma_device_type_duplex) {
38091  ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
38092  }
38093  return result;
38094  }
38095  }
38096 
38097  return MA_SUCCESS;
38098 }
38099 
38100 static ma_result ma_device_stop__aaudio(ma_device* pDevice)
38101 {
38102  MA_ASSERT(pDevice != NULL);
38103 
38104  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38105  ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
38106  if (result != MA_SUCCESS) {
38107  return result;
38108  }
38109  }
38110 
38111  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38112  ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
38113  if (result != MA_SUCCESS) {
38114  return result;
38115  }
38116  }
38117 
38118  ma_device__on_notification_stopped(pDevice);
38119 
38120  return MA_SUCCESS;
38121 }
38122 
38123 static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType)
38124 {
38125  ma_result result;
38126 
38127  MA_ASSERT(pDevice != NULL);
38128 
38129  /* The first thing to do is close the streams. */
38130  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
38131  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
38132  pDevice->aaudio.pStreamCapture = NULL;
38133  }
38134 
38135  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38136  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
38137  pDevice->aaudio.pStreamPlayback = NULL;
38138  }
38139 
38140  /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */
38141  {
38142  ma_device_config deviceConfig;
38143  ma_device_descriptor descriptorPlayback;
38144  ma_device_descriptor descriptorCapture;
38145 
38146  deviceConfig = ma_device_config_init(deviceType);
38147  deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */
38148  deviceConfig.playback.shareMode = pDevice->playback.shareMode;
38149  deviceConfig.playback.format = pDevice->playback.format;
38150  deviceConfig.playback.channels = pDevice->playback.channels;
38151  deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */
38152  deviceConfig.capture.shareMode = pDevice->capture.shareMode;
38153  deviceConfig.capture.format = pDevice->capture.format;
38154  deviceConfig.capture.channels = pDevice->capture.channels;
38155  deviceConfig.sampleRate = pDevice->sampleRate;
38156  deviceConfig.aaudio.usage = pDevice->aaudio.usage;
38157  deviceConfig.aaudio.contentType = pDevice->aaudio.contentType;
38158  deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset;
38159  deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy;
38160  deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute;
38161  deviceConfig.periods = 1;
38162 
38163  /* Try to get an accurate period size. */
38164  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38165  deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
38166  } else {
38167  deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
38168  }
38169 
38170  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
38171  descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID;
38172  descriptorCapture.shareMode = deviceConfig.capture.shareMode;
38173  descriptorCapture.format = deviceConfig.capture.format;
38174  descriptorCapture.channels = deviceConfig.capture.channels;
38175  descriptorCapture.sampleRate = deviceConfig.sampleRate;
38176  descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames;
38177  descriptorCapture.periodCount = deviceConfig.periods;
38178  }
38179 
38180  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38181  descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID;
38182  descriptorPlayback.shareMode = deviceConfig.playback.shareMode;
38183  descriptorPlayback.format = deviceConfig.playback.format;
38184  descriptorPlayback.channels = deviceConfig.playback.channels;
38185  descriptorPlayback.sampleRate = deviceConfig.sampleRate;
38186  descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames;
38187  descriptorPlayback.periodCount = deviceConfig.periods;
38188  }
38189 
38190  result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture);
38191  if (result != MA_SUCCESS) {
38192  return result;
38193  }
38194 
38195  result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture);
38196  if (result != MA_SUCCESS) {
38197  ma_device_uninit__aaudio(pDevice);
38198  return result;
38199  }
38200 
38201  /* We'll only ever do this in response to a reroute. */
38202  ma_device__on_notification_rerouted(pDevice);
38203 
38204  /* If the device is started, start the streams. Maybe make this configurable? */
38205  if (ma_device_get_state(pDevice) == ma_device_state_started) {
38206  if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) {
38207  ma_device_start__aaudio(pDevice);
38208  } else {
38209  ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */
38210  }
38211  }
38212 
38213  return MA_SUCCESS;
38214  }
38215 }
38216 
38217 static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
38218 {
38219  ma_AAudioStream* pStream = NULL;
38220 
38221  MA_ASSERT(pDevice != NULL);
38222  MA_ASSERT(type != ma_device_type_duplex);
38223  MA_ASSERT(pDeviceInfo != NULL);
38224 
38225  if (type == ma_device_type_playback) {
38226  pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture;
38227  pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio;
38228  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
38229  }
38230  if (type == ma_device_type_capture) {
38231  pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback;
38232  pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio;
38233  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
38234  }
38235 
38236  /* Safety. Should never happen. */
38237  if (pStream == NULL) {
38238  return MA_INVALID_OPERATION;
38239  }
38240 
38241  pDeviceInfo->nativeDataFormatCount = 0;
38242  ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo);
38243 
38244  return MA_SUCCESS;
38245 }
38246 
38247 
38248 static ma_result ma_context_uninit__aaudio(ma_context* pContext)
38249 {
38250  MA_ASSERT(pContext != NULL);
38251  MA_ASSERT(pContext->backend == ma_backend_aaudio);
38252 
38253  ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks);
38254 
38255  ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio);
38256  pContext->aaudio.hAAudio = NULL;
38257 
38258  return MA_SUCCESS;
38259 }
38260 
38261 static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
38262 {
38263  size_t i;
38264  const char* libNames[] = {
38265  "libaaudio.so"
38266  };
38267 
38268  for (i = 0; i < ma_countof(libNames); ++i) {
38269  pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]);
38270  if (pContext->aaudio.hAAudio != NULL) {
38271  break;
38272  }
38273  }
38274 
38275  if (pContext->aaudio.hAAudio == NULL) {
38276  return MA_FAILED_TO_INIT_BACKEND;
38277  }
38278 
38279  pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
38280  pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
38281  pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
38282  pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
38283  pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
38284  pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
38285  pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
38286  pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
38287  pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
38288  pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
38289  pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
38290  pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
38291  pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
38292  pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage");
38293  pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType");
38294  pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset");
38295  pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy");
38296  pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
38297  pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close");
38298  pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState");
38299  pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
38300  pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat");
38301  pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
38302  pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
38303  pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
38304  pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
38305  pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
38306  pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart");
38307  pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop");
38308 
38309 
38310  pCallbacks->onContextInit = ma_context_init__aaudio;
38311  pCallbacks->onContextUninit = ma_context_uninit__aaudio;
38312  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;
38313  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio;
38314  pCallbacks->onDeviceInit = ma_device_init__aaudio;
38315  pCallbacks->onDeviceUninit = ma_device_uninit__aaudio;
38316  pCallbacks->onDeviceStart = ma_device_start__aaudio;
38317  pCallbacks->onDeviceStop = ma_device_stop__aaudio;
38318  pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */
38319  pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */
38320  pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */
38321  pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio;
38322 
38323 
38324  /* We need a job thread so we can deal with rerouting. */
38325  {
38326  ma_result result;
38327  ma_device_job_thread_config jobThreadConfig;
38328 
38329  jobThreadConfig = ma_device_job_thread_config_init();
38330 
38331  result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread);
38332  if (result != MA_SUCCESS) {
38333  ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio);
38334  pContext->aaudio.hAAudio = NULL;
38335  return result;
38336  }
38337  }
38338 
38339 
38340  (void)pConfig;
38341  return MA_SUCCESS;
38342 }
38343 
38344 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
38345 {
38346  ma_device* pDevice;
38347 
38348  MA_ASSERT(pJob != NULL);
38349 
38350  pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice;
38351  MA_ASSERT(pDevice != NULL);
38352 
38353  /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */
38354  return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
38355 }
38356 #else
38357 /* Getting here means there is no AAudio backend so we need a no-op job implementation. */
38358 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
38359 {
38360  return ma_job_process__noop(pJob);
38361 }
38362 #endif /* AAudio */
38363 
38364 
38365 /******************************************************************************
38366 
38367 OpenSL|ES Backend
38368 
38369 ******************************************************************************/
38370 #ifdef MA_HAS_OPENSL
38371 #include <SLES/OpenSLES.h>
38372 #ifdef MA_ANDROID
38373 #include <SLES/OpenSLES_Android.h>
38374 #endif
38375 
38376 typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
38377 
38378 /* OpenSL|ES has one-per-application objects :( */
38379 static SLObjectItf g_maEngineObjectSL = NULL;
38380 static SLEngineItf g_maEngineSL = NULL;
38381 static ma_uint32 g_maOpenSLInitCounter = 0;
38382 static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */
38383 
38384 #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
38385 #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
38386 #define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
38387 #define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
38388 
38389 #ifdef MA_ANDROID
38390 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
38391 #else
38392 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
38393 #endif
38394 
38395 static ma_result ma_result_from_OpenSL(SLuint32 result)
38396 {
38397  switch (result)
38398  {
38399  case SL_RESULT_SUCCESS: return MA_SUCCESS;
38400  case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
38401  case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
38402  case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
38403  case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
38404  case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
38405  case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
38406  case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
38407  case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
38408  case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
38409  case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
38410  case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
38411  case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
38412  case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
38413  case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
38414  case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
38415  case SL_RESULT_CONTROL_LOST: return MA_ERROR;
38416  default: return MA_ERROR;
38417  }
38418 }
38419 
38420 /* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
38421 static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
38422 {
38423  switch (id)
38424  {
38425  case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
38426  case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
38427  case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
38428  case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
38429  case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
38430  case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
38431  case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
38432  case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
38433  case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
38434  case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
38435  case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
38436  case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
38437  case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
38438  case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
38439  case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
38440  case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
38441  case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
38442  case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
38443  default: return 0;
38444  }
38445 }
38446 
38447 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
38448 static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
38449 {
38450  switch (id)
38451  {
38452  case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
38453  case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
38454  case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
38455  case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
38456  case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
38457  case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
38458  case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
38459  case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
38460  case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
38461  case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
38462  case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
38463  case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
38464  case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
38465  case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
38466  case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
38467  case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
38468  case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
38469  case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
38470  case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
38471  default: return 0;
38472  }
38473 }
38474 
38475 /* Converts a channel mapping to an OpenSL-style channel mask. */
38476 static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)
38477 {
38478  SLuint32 channelMask = 0;
38479  ma_uint32 iChannel;
38480  for (iChannel = 0; iChannel < channels; ++iChannel) {
38481  channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);
38482  }
38483 
38484  return channelMask;
38485 }
38486 
38487 /* Converts an OpenSL-style channel mask to a miniaudio channel map. */
38488 static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)
38489 {
38490  if (channels == 1 && channelMask == 0) {
38491  pChannelMap[0] = MA_CHANNEL_MONO;
38492  } else if (channels == 2 && channelMask == 0) {
38493  pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
38494  pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38495  } else {
38496  if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
38497  pChannelMap[0] = MA_CHANNEL_MONO;
38498  } else {
38499  /* Just iterate over each bit. */
38500  ma_uint32 iChannel = 0;
38501  ma_uint32 iBit;
38502  for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
38503  SLuint32 bitValue = (channelMask & (1UL << iBit));
38504  if (bitValue != 0) {
38505  /* The bit is set. */
38506  pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
38507  iChannel += 1;
38508  }
38509  }
38510  }
38511  }
38512 }
38513 
38514 static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
38515 {
38516  if (samplesPerSec <= SL_SAMPLINGRATE_8) {
38517  return SL_SAMPLINGRATE_8;
38518  }
38519  if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
38520  return SL_SAMPLINGRATE_11_025;
38521  }
38522  if (samplesPerSec <= SL_SAMPLINGRATE_12) {
38523  return SL_SAMPLINGRATE_12;
38524  }
38525  if (samplesPerSec <= SL_SAMPLINGRATE_16) {
38526  return SL_SAMPLINGRATE_16;
38527  }
38528  if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
38529  return SL_SAMPLINGRATE_22_05;
38530  }
38531  if (samplesPerSec <= SL_SAMPLINGRATE_24) {
38532  return SL_SAMPLINGRATE_24;
38533  }
38534  if (samplesPerSec <= SL_SAMPLINGRATE_32) {
38535  return SL_SAMPLINGRATE_32;
38536  }
38537  if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
38538  return SL_SAMPLINGRATE_44_1;
38539  }
38540  if (samplesPerSec <= SL_SAMPLINGRATE_48) {
38541  return SL_SAMPLINGRATE_48;
38542  }
38543 
38544  /* Android doesn't support more than 48000. */
38545 #ifndef MA_ANDROID
38546  if (samplesPerSec <= SL_SAMPLINGRATE_64) {
38547  return SL_SAMPLINGRATE_64;
38548  }
38549  if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
38550  return SL_SAMPLINGRATE_88_2;
38551  }
38552  if (samplesPerSec <= SL_SAMPLINGRATE_96) {
38553  return SL_SAMPLINGRATE_96;
38554  }
38555  if (samplesPerSec <= SL_SAMPLINGRATE_192) {
38556  return SL_SAMPLINGRATE_192;
38557  }
38558 #endif
38559 
38560  return SL_SAMPLINGRATE_16;
38561 }
38562 
38563 
38564 static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType)
38565 {
38566  switch (streamType) {
38567  case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE;
38568  case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM;
38569  case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING;
38570  case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA;
38571  case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM;
38572  case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION;
38573  default: break;
38574  }
38575 
38576  return SL_ANDROID_STREAM_VOICE;
38577 }
38578 
38579 static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset)
38580 {
38581  switch (recordingPreset) {
38582  case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC;
38583  case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER;
38584  case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
38585  case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
38586  case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED;
38587  default: break;
38588  }
38589 
38590  return SL_ANDROID_RECORDING_PRESET_NONE;
38591 }
38592 
38593 
38594 static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
38595 {
38596  ma_bool32 cbResult;
38597 
38598  MA_ASSERT(pContext != NULL);
38599  MA_ASSERT(callback != NULL);
38600 
38601  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. */
38602  if (g_maOpenSLInitCounter == 0) {
38603  return MA_INVALID_OPERATION;
38604  }
38605 
38606  /*
38607  TODO: Test Me.
38608 
38609  This is currently untested, so for now we are just returning default devices.
38610  */
38611 #if 0 && !defined(MA_ANDROID)
38612  ma_bool32 isTerminated = MA_FALSE;
38613 
38614  SLuint32 pDeviceIDs[128];
38615  SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
38616 
38617  SLAudioIODeviceCapabilitiesItf deviceCaps;
38618  SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
38619  if (resultSL != SL_RESULT_SUCCESS) {
38620  /* The interface may not be supported so just report a default device. */
38621  goto return_default_device;
38622  }
38623 
38624  /* Playback */
38625  if (!isTerminated) {
38626  resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
38627  if (resultSL != SL_RESULT_SUCCESS) {
38628  return ma_result_from_OpenSL(resultSL);
38629  }
38630 
38631  for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
38632  ma_device_info deviceInfo;
38633  MA_ZERO_OBJECT(&deviceInfo);
38634  deviceInfo.id.opensl = pDeviceIDs[iDevice];
38635 
38636  SLAudioOutputDescriptor desc;
38637  resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
38638  if (resultSL == SL_RESULT_SUCCESS) {
38639  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
38640 
38641  ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
38642  if (cbResult == MA_FALSE) {
38643  isTerminated = MA_TRUE;
38644  break;
38645  }
38646  }
38647  }
38648  }
38649 
38650  /* Capture */
38651  if (!isTerminated) {
38652  resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
38653  if (resultSL != SL_RESULT_SUCCESS) {
38654  return ma_result_from_OpenSL(resultSL);
38655  }
38656 
38657  for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
38658  ma_device_info deviceInfo;
38659  MA_ZERO_OBJECT(&deviceInfo);
38660  deviceInfo.id.opensl = pDeviceIDs[iDevice];
38661 
38662  SLAudioInputDescriptor desc;
38663  resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
38664  if (resultSL == SL_RESULT_SUCCESS) {
38665  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
38666 
38667  ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
38668  if (cbResult == MA_FALSE) {
38669  isTerminated = MA_TRUE;
38670  break;
38671  }
38672  }
38673  }
38674  }
38675 
38676  return MA_SUCCESS;
38677 #else
38678  goto return_default_device;
38679 #endif
38680 
38681 return_default_device:;
38682  cbResult = MA_TRUE;
38683 
38684  /* Playback. */
38685  if (cbResult) {
38686  ma_device_info deviceInfo;
38687  MA_ZERO_OBJECT(&deviceInfo);
38688  deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
38689  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
38690  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
38691  }
38692 
38693  /* Capture. */
38694  if (cbResult) {
38695  ma_device_info deviceInfo;
38696  MA_ZERO_OBJECT(&deviceInfo);
38697  deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
38698  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
38699  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
38700  }
38701 
38702  return MA_SUCCESS;
38703 }
38704 
38705 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)
38706 {
38707  MA_ASSERT(pContext != NULL);
38708  MA_ASSERT(pDeviceInfo != NULL);
38709 
38710  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
38711  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
38712  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
38713  pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
38714  pDeviceInfo->nativeDataFormatCount += 1;
38715 }
38716 
38717 static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)
38718 {
38719  ma_uint32 minChannels = 1;
38720  ma_uint32 maxChannels = 2;
38721  ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000;
38722  ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000;
38723  ma_uint32 iChannel;
38724  ma_uint32 iSampleRate;
38725 
38726  MA_ASSERT(pContext != NULL);
38727  MA_ASSERT(pDeviceInfo != NULL);
38728 
38729  /*
38730  Each sample format can support mono and stereo, and we'll support a small subset of standard
38731  rates (up to 48000). A better solution would be to somehow find a native sample rate.
38732  */
38733  for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {
38734  for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
38735  ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
38736  if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
38737  ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);
38738  }
38739  }
38740  }
38741 }
38742 
38743 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)
38744 {
38745  MA_ASSERT(pContext != NULL);
38746 
38747  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. */
38748  if (g_maOpenSLInitCounter == 0) {
38749  return MA_INVALID_OPERATION;
38750  }
38751 
38752  /*
38753  TODO: Test Me.
38754 
38755  This is currently untested, so for now we are just returning default devices.
38756  */
38757 #if 0 && !defined(MA_ANDROID)
38758  SLAudioIODeviceCapabilitiesItf deviceCaps;
38759  SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
38760  if (resultSL != SL_RESULT_SUCCESS) {
38761  /* The interface may not be supported so just report a default device. */
38762  goto return_default_device;
38763  }
38764 
38765  if (deviceType == ma_device_type_playback) {
38766  SLAudioOutputDescriptor desc;
38767  resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
38768  if (resultSL != SL_RESULT_SUCCESS) {
38769  return ma_result_from_OpenSL(resultSL);
38770  }
38771 
38772  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
38773  } else {
38774  SLAudioInputDescriptor desc;
38775  resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
38776  if (resultSL != SL_RESULT_SUCCESS) {
38777  return ma_result_from_OpenSL(resultSL);
38778  }
38779 
38780  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
38781  }
38782 
38783  goto return_detailed_info;
38784 #else
38785  goto return_default_device;
38786 #endif
38787 
38788 return_default_device:
38789  if (pDeviceID != NULL) {
38790  if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
38791  (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
38792  return MA_NO_DEVICE; /* Don't know the device. */
38793  }
38794  }
38795 
38796  /* ID and Name / Description */
38797  if (deviceType == ma_device_type_playback) {
38798  pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
38799  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
38800  } else {
38801  pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
38802  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
38803  }
38804 
38805  pDeviceInfo->isDefault = MA_TRUE;
38806 
38807  goto return_detailed_info;
38808 
38809 
38810 return_detailed_info:
38811 
38812  /*
38813  For now we're just outputting a set of values that are supported by the API but not necessarily supported
38814  by the device natively. Later on we should work on this so that it more closely reflects the device's
38815  actual native format.
38816  */
38817  pDeviceInfo->nativeDataFormatCount = 0;
38818 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
38819  ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);
38820 #endif
38821  ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);
38822  ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo);
38823 
38824  return MA_SUCCESS;
38825 }
38826 
38827 
38828 #ifdef MA_ANDROID
38829 /*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
38830 static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
38831 {
38832  ma_device* pDevice = (ma_device*)pUserData;
38833  size_t periodSizeInBytes;
38834  ma_uint8* pBuffer;
38835  SLresult resultSL;
38836 
38837  MA_ASSERT(pDevice != NULL);
38838 
38839  (void)pBufferQueue;
38840 
38841  /*
38842  For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
38843  OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
38844  but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
38845  */
38846 
38847  /* Don't do anything if the device is not started. */
38848  if (ma_device_get_state(pDevice) != ma_device_state_started) {
38849  return;
38850  }
38851 
38852  /* Don't do anything if the device is being drained. */
38853  if (pDevice->opensl.isDrainingCapture) {
38854  return;
38855  }
38856 
38857  periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
38858  pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
38859 
38860  ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames);
38861 
38862  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
38863  if (resultSL != SL_RESULT_SUCCESS) {
38864  return;
38865  }
38866 
38867  pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
38868 }
38869 
38870 static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
38871 {
38872  ma_device* pDevice = (ma_device*)pUserData;
38873  size_t periodSizeInBytes;
38874  ma_uint8* pBuffer;
38875  SLresult resultSL;
38876 
38877  MA_ASSERT(pDevice != NULL);
38878 
38879  (void)pBufferQueue;
38880 
38881  /* Don't do anything if the device is not started. */
38882  if (ma_device_get_state(pDevice) != ma_device_state_started) {
38883  return;
38884  }
38885 
38886  /* Don't do anything if the device is being drained. */
38887  if (pDevice->opensl.isDrainingPlayback) {
38888  return;
38889  }
38890 
38891  periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
38892  pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
38893 
38894  ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames);
38895 
38896  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
38897  if (resultSL != SL_RESULT_SUCCESS) {
38898  return;
38899  }
38900 
38901  pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
38902 }
38903 #endif
38904 
38905 static ma_result ma_device_uninit__opensl(ma_device* pDevice)
38906 {
38907  MA_ASSERT(pDevice != NULL);
38908 
38909  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. */
38910  if (g_maOpenSLInitCounter == 0) {
38911  return MA_INVALID_OPERATION;
38912  }
38913 
38914  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38915  if (pDevice->opensl.pAudioRecorderObj) {
38916  MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
38917  }
38918 
38919  ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
38920  }
38921 
38922  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38923  if (pDevice->opensl.pAudioPlayerObj) {
38924  MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
38925  }
38926  if (pDevice->opensl.pOutputMixObj) {
38927  MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
38928  }
38929 
38930  ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
38931  }
38932 
38933  return MA_SUCCESS;
38934 }
38935 
38936 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
38937 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
38938 #else
38939 typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
38940 #endif
38941 
38942 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)
38943 {
38944  /* We need to convert our format/channels/rate so that they aren't set to default. */
38945  if (format == ma_format_unknown) {
38946  format = MA_DEFAULT_FORMAT;
38947  }
38948  if (channels == 0) {
38949  channels = MA_DEFAULT_CHANNELS;
38950  }
38951  if (sampleRate == 0) {
38952  sampleRate = MA_DEFAULT_SAMPLE_RATE;
38953  }
38954 
38955 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
38956  if (format == ma_format_f32) {
38957  pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
38958  pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
38959  } else {
38960  pDataFormat->formatType = SL_DATAFORMAT_PCM;
38961  }
38962 #else
38963  pDataFormat->formatType = SL_DATAFORMAT_PCM;
38964 #endif
38965 
38966  pDataFormat->numChannels = channels;
38967  ((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 */
38968  pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8;
38969  pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
38970  pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
38971 
38972  /*
38973  Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
38974  - Only mono and stereo is supported.
38975  - Only u8 and s16 formats are supported.
38976  - Maximum sample rate of 48000.
38977  */
38978 #ifdef MA_ANDROID
38979  if (pDataFormat->numChannels > 2) {
38980  pDataFormat->numChannels = 2;
38981  }
38982 #if __ANDROID_API__ >= 21
38983  if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
38984  /* It's floating point. */
38985  MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
38986  if (pDataFormat->bitsPerSample > 32) {
38987  pDataFormat->bitsPerSample = 32;
38988  }
38989  } else {
38990  if (pDataFormat->bitsPerSample > 16) {
38991  pDataFormat->bitsPerSample = 16;
38992  }
38993  }
38994 #else
38995  if (pDataFormat->bitsPerSample > 16) {
38996  pDataFormat->bitsPerSample = 16;
38997  }
38998 #endif
38999  if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
39000  ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
39001  }
39002 #endif
39003 
39004  pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
39005 
39006  return MA_SUCCESS;
39007 }
39008 
39009 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)
39010 {
39011  ma_bool32 isFloatingPoint = MA_FALSE;
39012 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
39013  if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
39014  MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
39015  isFloatingPoint = MA_TRUE;
39016  }
39017 #endif
39018  if (isFloatingPoint) {
39019  if (pDataFormat->bitsPerSample == 32) {
39020  *pFormat = ma_format_f32;
39021  }
39022  } else {
39023  if (pDataFormat->bitsPerSample == 8) {
39024  *pFormat = ma_format_u8;
39025  } else if (pDataFormat->bitsPerSample == 16) {
39026  *pFormat = ma_format_s16;
39027  } else if (pDataFormat->bitsPerSample == 24) {
39028  *pFormat = ma_format_s24;
39029  } else if (pDataFormat->bitsPerSample == 32) {
39030  *pFormat = ma_format_s32;
39031  }
39032  }
39033 
39034  *pChannels = pDataFormat->numChannels;
39035  *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
39036  ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
39037 
39038  return MA_SUCCESS;
39039 }
39040 
39041 static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
39042 {
39043 #ifdef MA_ANDROID
39044  SLDataLocator_AndroidSimpleBufferQueue queue;
39045  SLresult resultSL;
39046  size_t bufferSizeInBytes;
39047  SLInterfaceID itfIDs[2];
39048  const SLboolean itfIDsRequired[] = {
39049  SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */
39050  SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */
39051  };
39052 #endif
39053 
39054  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. */
39055  if (g_maOpenSLInitCounter == 0) {
39056  return MA_INVALID_OPERATION;
39057  }
39058 
39059  if (pConfig->deviceType == ma_device_type_loopback) {
39060  return MA_DEVICE_TYPE_NOT_SUPPORTED;
39061  }
39062 
39063  /*
39064  For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
39065  been able to test with and I currently depend on Android-specific extensions (simple buffer
39066  queues).
39067  */
39068 #ifdef MA_ANDROID
39069  itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
39070  itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION;
39071 
39072  /* No exclusive mode with OpenSL|ES. */
39073  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
39074  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
39075  return MA_SHARE_MODE_NOT_SUPPORTED;
39076  }
39077 
39078  /* Now we can start initializing the device properly. */
39079  MA_ASSERT(pDevice != NULL);
39080  MA_ZERO_OBJECT(&pDevice->opensl);
39081 
39082  queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
39083 
39084  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
39085  ma_SLDataFormat_PCM pcm;
39086  SLDataLocator_IODevice locatorDevice;
39087  SLDataSource source;
39088  SLDataSink sink;
39089  SLAndroidConfigurationItf pRecorderConfig;
39090 
39091  ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);
39092 
39093  locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
39094  locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
39095  locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */
39096  locatorDevice.device = NULL;
39097 
39098  source.pLocator = &locatorDevice;
39099  source.pFormat = NULL;
39100 
39101  queue.numBuffers = pDescriptorCapture->periodCount;
39102 
39103  sink.pLocator = &queue;
39104  sink.pFormat = (SLDataFormat_PCM*)&pcm;
39105 
39106  resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
39107  if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
39108  /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
39109  pcm.formatType = SL_DATAFORMAT_PCM;
39110  pcm.numChannels = 1;
39111  ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
39112  pcm.bitsPerSample = 16;
39113  pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
39114  pcm.channelMask = 0;
39115  resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
39116  }
39117 
39118  if (resultSL != SL_RESULT_SUCCESS) {
39119  ma_device_uninit__opensl(pDevice);
39120  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.");
39121  return ma_result_from_OpenSL(resultSL);
39122  }
39123 
39124 
39125  /* Set the recording preset before realizing the player. */
39126  if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) {
39127  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);
39128  if (resultSL == SL_RESULT_SUCCESS) {
39129  SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);
39130  resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));
39131  if (resultSL != SL_RESULT_SUCCESS) {
39132  /* Failed to set the configuration. Just keep going. */
39133  }
39134  }
39135  }
39136 
39137  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
39138  if (resultSL != SL_RESULT_SUCCESS) {
39139  ma_device_uninit__opensl(pDevice);
39140  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.");
39141  return ma_result_from_OpenSL(resultSL);
39142  }
39143 
39144  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
39145  if (resultSL != SL_RESULT_SUCCESS) {
39146  ma_device_uninit__opensl(pDevice);
39147  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.");
39148  return ma_result_from_OpenSL(resultSL);
39149  }
39150 
39151  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
39152  if (resultSL != SL_RESULT_SUCCESS) {
39153  ma_device_uninit__opensl(pDevice);
39154  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
39155  return ma_result_from_OpenSL(resultSL);
39156  }
39157 
39158  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
39159  if (resultSL != SL_RESULT_SUCCESS) {
39160  ma_device_uninit__opensl(pDevice);
39161  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
39162  return ma_result_from_OpenSL(resultSL);
39163  }
39164 
39165  /* The internal format is determined by the "pcm" object. */
39166  ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));
39167 
39168  /* Buffer. */
39169  pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
39170  pDevice->opensl.currentBufferIndexCapture = 0;
39171 
39172  bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;
39173  pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
39174  if (pDevice->opensl.pBufferCapture == NULL) {
39175  ma_device_uninit__opensl(pDevice);
39176  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
39177  return MA_OUT_OF_MEMORY;
39178  }
39179  MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
39180  }
39181 
39182  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
39183  ma_SLDataFormat_PCM pcm;
39184  SLDataSource source;
39185  SLDataLocator_OutputMix outmixLocator;
39186  SLDataSink sink;
39187  SLAndroidConfigurationItf pPlayerConfig;
39188 
39189  ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);
39190 
39191  resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
39192  if (resultSL != SL_RESULT_SUCCESS) {
39193  ma_device_uninit__opensl(pDevice);
39194  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.");
39195  return ma_result_from_OpenSL(resultSL);
39196  }
39197 
39198  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
39199  if (resultSL != SL_RESULT_SUCCESS) {
39200  ma_device_uninit__opensl(pDevice);
39201  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.");
39202  return ma_result_from_OpenSL(resultSL);
39203  }
39204 
39205  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
39206  if (resultSL != SL_RESULT_SUCCESS) {
39207  ma_device_uninit__opensl(pDevice);
39208  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.");
39209  return ma_result_from_OpenSL(resultSL);
39210  }
39211 
39212  /* Set the output device. */
39213  if (pDescriptorPlayback->pDeviceID != NULL) {
39214  SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;
39215  MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
39216  }
39217 
39218  queue.numBuffers = pDescriptorPlayback->periodCount;
39219 
39220  source.pLocator = &queue;
39221  source.pFormat = (SLDataFormat_PCM*)&pcm;
39222 
39223  outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
39224  outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
39225 
39226  sink.pLocator = &outmixLocator;
39227  sink.pFormat = NULL;
39228 
39229  resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
39230  if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
39231  /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
39232  pcm.formatType = SL_DATAFORMAT_PCM;
39233  pcm.numChannels = 2;
39234  ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
39235  pcm.bitsPerSample = 16;
39236  pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
39237  pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
39238  resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
39239  }
39240 
39241  if (resultSL != SL_RESULT_SUCCESS) {
39242  ma_device_uninit__opensl(pDevice);
39243  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.");
39244  return ma_result_from_OpenSL(resultSL);
39245  }
39246 
39247 
39248  /* Set the stream type before realizing the player. */
39249  if (pConfig->opensl.streamType != ma_opensl_stream_type_default) {
39250  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);
39251  if (resultSL == SL_RESULT_SUCCESS) {
39252  SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);
39253  resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
39254  if (resultSL != SL_RESULT_SUCCESS) {
39255  /* Failed to set the configuration. Just keep going. */
39256  }
39257  }
39258  }
39259 
39260  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
39261  if (resultSL != SL_RESULT_SUCCESS) {
39262  ma_device_uninit__opensl(pDevice);
39263  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.");
39264  return ma_result_from_OpenSL(resultSL);
39265  }
39266 
39267  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
39268  if (resultSL != SL_RESULT_SUCCESS) {
39269  ma_device_uninit__opensl(pDevice);
39270  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.");
39271  return ma_result_from_OpenSL(resultSL);
39272  }
39273 
39274  resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
39275  if (resultSL != SL_RESULT_SUCCESS) {
39276  ma_device_uninit__opensl(pDevice);
39277  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
39278  return ma_result_from_OpenSL(resultSL);
39279  }
39280 
39281  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
39282  if (resultSL != SL_RESULT_SUCCESS) {
39283  ma_device_uninit__opensl(pDevice);
39284  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
39285  return ma_result_from_OpenSL(resultSL);
39286  }
39287 
39288  /* The internal format is determined by the "pcm" object. */
39289  ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap));
39290 
39291  /* Buffer. */
39292  pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
39293  pDevice->opensl.currentBufferIndexPlayback = 0;
39294 
39295  bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount;
39296  pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
39297  if (pDevice->opensl.pBufferPlayback == NULL) {
39298  ma_device_uninit__opensl(pDevice);
39299  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
39300  return MA_OUT_OF_MEMORY;
39301  }
39302  MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
39303  }
39304 
39305  return MA_SUCCESS;
39306 #else
39307  return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
39308 #endif
39309 }
39310 
39311 static ma_result ma_device_start__opensl(ma_device* pDevice)
39312 {
39313  SLresult resultSL;
39314  size_t periodSizeInBytes;
39315  ma_uint32 iPeriod;
39316 
39317  MA_ASSERT(pDevice != NULL);
39318 
39319  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. */
39320  if (g_maOpenSLInitCounter == 0) {
39321  return MA_INVALID_OPERATION;
39322  }
39323 
39324  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
39325  resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
39326  if (resultSL != SL_RESULT_SUCCESS) {
39327  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.");
39328  return ma_result_from_OpenSL(resultSL);
39329  }
39330 
39331  periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
39332  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
39333  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
39334  if (resultSL != SL_RESULT_SUCCESS) {
39335  MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
39336  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.");
39337  return ma_result_from_OpenSL(resultSL);
39338  }
39339  }
39340  }
39341 
39342  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
39343  resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
39344  if (resultSL != SL_RESULT_SUCCESS) {
39345  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.");
39346  return ma_result_from_OpenSL(resultSL);
39347  }
39348 
39349  /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */
39350  if (pDevice->type == ma_device_type_duplex) {
39351  MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
39352  } else {
39353  ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
39354  }
39355 
39356  periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
39357  for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
39358  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
39359  if (resultSL != SL_RESULT_SUCCESS) {
39360  MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
39361  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.");
39362  return ma_result_from_OpenSL(resultSL);
39363  }
39364  }
39365  }
39366 
39367  return MA_SUCCESS;
39368 }
39369 
39370 static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
39371 {
39372  SLAndroidSimpleBufferQueueItf pBufferQueue;
39373 
39374  MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
39375 
39376  if (pDevice->type == ma_device_type_capture) {
39377  pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
39378  pDevice->opensl.isDrainingCapture = MA_TRUE;
39379  } else {
39380  pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
39381  pDevice->opensl.isDrainingPlayback = MA_TRUE;
39382  }
39383 
39384  for (;;) {
39385  SLAndroidSimpleBufferQueueState state;
39386 
39387  MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
39388  if (state.count == 0) {
39389  break;
39390  }
39391 
39392  ma_sleep(10);
39393  }
39394 
39395  if (pDevice->type == ma_device_type_capture) {
39396  pDevice->opensl.isDrainingCapture = MA_FALSE;
39397  } else {
39398  pDevice->opensl.isDrainingPlayback = MA_FALSE;
39399  }
39400 
39401  return MA_SUCCESS;
39402 }
39403 
39404 static ma_result ma_device_stop__opensl(ma_device* pDevice)
39405 {
39406  SLresult resultSL;
39407 
39408  MA_ASSERT(pDevice != NULL);
39409 
39410  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. */
39411  if (g_maOpenSLInitCounter == 0) {
39412  return MA_INVALID_OPERATION;
39413  }
39414 
39415  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
39416  ma_device_drain__opensl(pDevice, ma_device_type_capture);
39417 
39418  resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
39419  if (resultSL != SL_RESULT_SUCCESS) {
39420  ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.");
39421  return ma_result_from_OpenSL(resultSL);
39422  }
39423 
39424  MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
39425  }
39426 
39427  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
39428  ma_device_drain__opensl(pDevice, ma_device_type_playback);
39429 
39430  resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_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 playback device.");
39433  return ma_result_from_OpenSL(resultSL);
39434  }
39435 
39436  MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
39437  }
39438 
39439  /* 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. */
39440  ma_device__on_notification_stopped(pDevice);
39441 
39442  return MA_SUCCESS;
39443 }
39444 
39445 
39446 static ma_result ma_context_uninit__opensl(ma_context* pContext)
39447 {
39448  MA_ASSERT(pContext != NULL);
39449  MA_ASSERT(pContext->backend == ma_backend_opensl);
39450  (void)pContext;
39451 
39452  /* Uninit global data. */
39453  ma_spinlock_lock(&g_maOpenSLSpinlock);
39454  {
39455  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(). */
39456 
39457  g_maOpenSLInitCounter -= 1;
39458  if (g_maOpenSLInitCounter == 0) {
39459  (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
39460  }
39461  }
39462  ma_spinlock_unlock(&g_maOpenSLSpinlock);
39463 
39464  return MA_SUCCESS;
39465 }
39466 
39467 static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)
39468 {
39469  /* 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. */
39470  ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName);
39471  if (p == NULL) {
39472  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName);
39473  return MA_NO_BACKEND;
39474  }
39475 
39476  *pHandle = *p;
39477  return MA_SUCCESS;
39478 }
39479 
39480 static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)
39481 {
39482  g_maOpenSLInitCounter += 1;
39483  if (g_maOpenSLInitCounter == 1) {
39484  SLresult resultSL;
39485 
39486  resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
39487  if (resultSL != SL_RESULT_SUCCESS) {
39488  g_maOpenSLInitCounter -= 1;
39489  return ma_result_from_OpenSL(resultSL);
39490  }
39491 
39492  (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
39493 
39494  resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);
39495  if (resultSL != SL_RESULT_SUCCESS) {
39496  (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
39497  g_maOpenSLInitCounter -= 1;
39498  return ma_result_from_OpenSL(resultSL);
39499  }
39500  }
39501 
39502  return MA_SUCCESS;
39503 }
39504 
39505 static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
39506 {
39507  ma_result result;
39508 
39509 #if !defined(MA_NO_RUNTIME_LINKING)
39510  size_t i;
39511  const char* libOpenSLESNames[] = {
39512  "libOpenSLES.so"
39513  };
39514 #endif
39515 
39516  MA_ASSERT(pContext != NULL);
39517 
39518  (void)pConfig;
39519 
39520 #if !defined(MA_NO_RUNTIME_LINKING)
39521  /*
39522  Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
39523  report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
39524  and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
39525  references to the symbols and will hopefully skip the checks.
39526  */
39527  for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
39528  pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]);
39529  if (pContext->opensl.libOpenSLES != NULL) {
39530  break;
39531  }
39532  }
39533 
39534  if (pContext->opensl.libOpenSLES == NULL) {
39535  ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so");
39536  return MA_NO_BACKEND;
39537  }
39538 
39539  result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
39540  if (result != MA_SUCCESS) {
39541  ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
39542  return result;
39543  }
39544 
39545  result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
39546  if (result != MA_SUCCESS) {
39547  ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
39548  return result;
39549  }
39550 
39551  result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
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_RECORD", &pContext->opensl.SL_IID_RECORD);
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_PLAY", &pContext->opensl.SL_IID_PLAY);
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_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
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_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);
39576  if (result != MA_SUCCESS) {
39577  ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
39578  return result;
39579  }
39580 
39581  pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine");
39582  if (pContext->opensl.slCreateEngine == NULL) {
39583  ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
39584  ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine.");
39585  return MA_NO_BACKEND;
39586  }
39587 #else
39588  pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE;
39589  pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;
39590  pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
39591  pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD;
39592  pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY;
39593  pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX;
39594  pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION;
39595  pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine;
39596 #endif
39597 
39598 
39599  /* Initialize global data first if applicable. */
39600  ma_spinlock_lock(&g_maOpenSLSpinlock);
39601  {
39602  result = ma_context_init_engine_nolock__opensl(pContext);
39603  }
39604  ma_spinlock_unlock(&g_maOpenSLSpinlock);
39605 
39606  if (result != MA_SUCCESS) {
39607  ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
39608  ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine.");
39609  return result;
39610  }
39611 
39612  pCallbacks->onContextInit = ma_context_init__opensl;
39613  pCallbacks->onContextUninit = ma_context_uninit__opensl;
39614  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;
39615  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl;
39616  pCallbacks->onDeviceInit = ma_device_init__opensl;
39617  pCallbacks->onDeviceUninit = ma_device_uninit__opensl;
39618  pCallbacks->onDeviceStart = ma_device_start__opensl;
39619  pCallbacks->onDeviceStop = ma_device_stop__opensl;
39620  pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */
39621  pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */
39622  pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */
39623 
39624  return MA_SUCCESS;
39625 }
39626 #endif /* OpenSL|ES */
39627 
39628 
39629 /******************************************************************************
39630 
39631 Web Audio Backend
39632 
39633 ******************************************************************************/
39634 #ifdef MA_HAS_WEBAUDIO
39635 #include <emscripten/emscripten.h>
39636 
39637 #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32)))
39638  #include <emscripten/webaudio.h>
39639  #define MA_SUPPORT_AUDIO_WORKLETS
39640 #endif
39641 
39642 /*
39643 TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS.
39644 */
39645 #if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS)
39646  #define MA_USE_AUDIO_WORKLETS
39647 #endif
39648 
39649 /* The thread stack size must be a multiple of 16. */
39650 #ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE
39651 #define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384
39652 #endif
39653 
39654 #if defined(MA_USE_AUDIO_WORKLETS)
39655 #define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced"
39656 #define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive"
39657 #define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback"
39658 #endif
39659 
39660 static ma_bool32 ma_is_capture_supported__webaudio()
39661 {
39662  return EM_ASM_INT({
39663  return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
39664  }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
39665 }
39666 
39667 #ifdef __cplusplus
39668 extern "C" {
39669 #endif
39670 void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
39671 {
39672  return ma_malloc(sz, pAllocationCallbacks);
39673 }
39674 
39675 void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
39676 {
39677  ma_free(p, pAllocationCallbacks);
39678 }
39679 
39680 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
39681 {
39682  ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);
39683 }
39684 
39685 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
39686 {
39687  ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount);
39688 }
39689 #ifdef __cplusplus
39690 }
39691 #endif
39692 
39693 static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
39694 {
39695  ma_bool32 cbResult = MA_TRUE;
39696 
39697  MA_ASSERT(pContext != NULL);
39698  MA_ASSERT(callback != NULL);
39699 
39700  /* Only supporting default devices for now. */
39701 
39702  /* Playback. */
39703  if (cbResult) {
39704  ma_device_info deviceInfo;
39705  MA_ZERO_OBJECT(&deviceInfo);
39706  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
39707  deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
39708  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
39709  }
39710 
39711  /* Capture. */
39712  if (cbResult) {
39713  if (ma_is_capture_supported__webaudio()) {
39714  ma_device_info deviceInfo;
39715  MA_ZERO_OBJECT(&deviceInfo);
39716  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
39717  deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
39718  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
39719  }
39720  }
39721 
39722  return MA_SUCCESS;
39723 }
39724 
39725 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)
39726 {
39727  MA_ASSERT(pContext != NULL);
39728 
39729  if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
39730  return MA_NO_DEVICE;
39731  }
39732 
39733  MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
39734 
39735  /* Only supporting default devices for now. */
39736  (void)pDeviceID;
39737  if (deviceType == ma_device_type_playback) {
39738  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
39739  } else {
39740  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
39741  }
39742 
39743  /* Only supporting default devices. */
39744  pDeviceInfo->isDefault = MA_TRUE;
39745 
39746  /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
39747  pDeviceInfo->nativeDataFormats[0].flags = 0;
39748  pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
39749  pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */
39750  pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({
39751  try {
39752  var temp = new (window.AudioContext || window.webkitAudioContext)();
39753  var sampleRate = temp.sampleRate;
39754  temp.close();
39755  return sampleRate;
39756  } catch(e) {
39757  return 0;
39758  }
39759  }, 0); /* Must pass in a dummy argument for C99 compatibility. */
39760 
39761  if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) {
39762  return MA_NO_DEVICE;
39763  }
39764 
39765  pDeviceInfo->nativeDataFormatCount = 1;
39766 
39767  return MA_SUCCESS;
39768 }
39769 
39770 static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
39771 {
39772  MA_ASSERT(pDevice != NULL);
39773 
39774  #if defined(MA_USE_AUDIO_WORKLETS)
39775  {
39776  EM_ASM({
39777  var device = miniaudio.get_device_by_index($0);
39778 
39779  if (device.streamNode !== undefined) {
39780  device.streamNode.disconnect();
39781  device.streamNode = undefined;
39782  }
39783  }, pDevice->webaudio.deviceIndex);
39784 
39785  emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet);
39786  emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
39787  ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks);
39788  }
39789  #else
39790  {
39791  EM_ASM({
39792  var device = miniaudio.get_device_by_index($0);
39793 
39794  /* Make sure all nodes are disconnected and marked for collection. */
39795  if (device.scriptNode !== undefined) {
39796  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... */
39797  device.scriptNode.disconnect();
39798  device.scriptNode = undefined;
39799  }
39800 
39801  if (device.streamNode !== undefined) {
39802  device.streamNode.disconnect();
39803  device.streamNode = undefined;
39804  }
39805 
39806  /*
39807  Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
39808  to clear the callback before closing.
39809  */
39810  device.webaudio.close();
39811  device.webaudio = undefined;
39812  device.pDevice = undefined;
39813  }, pDevice->webaudio.deviceIndex);
39814  }
39815  #endif
39816 
39817  /* Clean up the device on the JS side. */
39818  EM_ASM({
39819  miniaudio.untrack_device_by_index($0);
39820  }, pDevice->webaudio.deviceIndex);
39821 
39822  ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
39823 
39824  return MA_SUCCESS;
39825 }
39826 
39827 #if !defined(MA_USE_AUDIO_WORKLETS)
39828 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
39829 {
39830  /*
39831  There have been reports of the default buffer size being too small on some browsers. If we're using
39832  the default buffer size, we'll make sure the period size is bigger than our standard defaults.
39833  */
39834  ma_uint32 periodSizeInFrames;
39835 
39836  if (nativeSampleRate == 0) {
39837  nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
39838  }
39839 
39840  if (pDescriptor->periodSizeInFrames == 0) {
39841  if (pDescriptor->periodSizeInMilliseconds == 0) {
39842  if (performanceProfile == ma_performance_profile_low_latency) {
39843  periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */
39844  } else {
39845  periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate);
39846  }
39847  } else {
39848  periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
39849  }
39850  } else {
39851  periodSizeInFrames = pDescriptor->periodSizeInFrames;
39852  }
39853 
39854  /* The size of the buffer must be a power of 2 and between 256 and 16384. */
39855  if (periodSizeInFrames < 256) {
39856  periodSizeInFrames = 256;
39857  } else if (periodSizeInFrames > 16384) {
39858  periodSizeInFrames = 16384;
39859  } else {
39860  periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames);
39861  }
39862 
39863  return periodSizeInFrames;
39864 }
39865 #endif
39866 
39867 
39868 #if defined(MA_USE_AUDIO_WORKLETS)
39869 typedef struct
39870 {
39871  ma_device* pDevice;
39872  const ma_device_config* pConfig;
39873  ma_device_descriptor* pDescriptorPlayback;
39874  ma_device_descriptor* pDescriptorCapture;
39875 } ma_audio_worklet_thread_initialized_data;
39876 
39877 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)
39878 {
39879  ma_device* pDevice = (ma_device*)pUserData;
39880  ma_uint32 frameCount;
39881 
39882  (void)paramCount;
39883  (void)pParams;
39884 
39885  if (ma_device_get_state(pDevice) != ma_device_state_started) {
39886  return EM_TRUE;
39887  }
39888 
39889  /*
39890  The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels
39891  like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer
39892  to variables instead of a hard coded number. In any case, will follow along for the time being.
39893 
39894  Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio
39895  for further processing.
39896  */
39897  frameCount = 128;
39898 
39899  if (inputCount > 0) {
39900  /* Input data needs to be interleaved before we hand it to the client. */
39901  for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) {
39902  for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) {
39903  pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame];
39904  }
39905  }
39906 
39907  ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer);
39908  }
39909 
39910  if (outputCount > 0) {
39911  /* If it's a capture-only device, we'll need to output silence. */
39912  if (pDevice->type == ma_device_type_capture) {
39913  MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float));
39914  } else {
39915  ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer);
39916 
39917  /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */
39918  for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) {
39919  for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) {
39920  pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel];
39921  }
39922  }
39923  }
39924  }
39925 
39926  return EM_TRUE;
39927 }
39928 
39929 
39930 static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)
39931 {
39932  ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;
39933  EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions;
39934  int channels = 0;
39935  size_t intermediaryBufferSizeInFrames;
39936  int sampleRate;
39937 
39938  if (success == EM_FALSE) {
39939  pParameters->pDevice->webaudio.initResult = MA_ERROR;
39940  ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
39941  return;
39942  }
39943 
39944  /* The next step is to initialize the audio worklet node. */
39945  MA_ZERO_OBJECT(&audioWorkletOptions);
39946 
39947  /*
39948  The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel
39949  count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an
39950  output channel count on the capture side. This is slightly confusing for capture mode because intuitively you
39951  wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have
39952  proper control over the channel count. In the capture case, we'll have to output silence to it's output node.
39953  */
39954  if (pParameters->pConfig->deviceType == ma_device_type_capture) {
39955  channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS);
39956  audioWorkletOptions.numberOfInputs = 1;
39957  } else {
39958  channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS);
39959 
39960  if (pParameters->pConfig->deviceType == ma_device_type_duplex) {
39961  audioWorkletOptions.numberOfInputs = 1;
39962  } else {
39963  audioWorkletOptions.numberOfInputs = 0;
39964  }
39965  }
39966 
39967  audioWorkletOptions.numberOfOutputs = 1;
39968  audioWorkletOptions.outputChannelCounts = &channels;
39969 
39970 
39971  /*
39972  Now that we know the channel count to use we can allocate the intermediary buffer. The
39973  intermediary buffer is used for interleaving and deinterleaving.
39974  */
39975  intermediaryBufferSizeInFrames = 128;
39976 
39977  pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks);
39978  if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) {
39979  pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY;
39980  ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
39981  return;
39982  }
39983 
39984 
39985  pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice);
39986 
39987  /* With the audio worklet initialized we can now attach it to the graph. */
39988  if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) {
39989  ma_result attachmentResult = (ma_result)EM_ASM_INT({
39990  var getUserMediaResult = 0;
39991  var audioWorklet = emscriptenGetAudioObject($0);
39992  var audioContext = emscriptenGetAudioObject($1);
39993 
39994  navigator.mediaDevices.getUserMedia({audio:true, video:false})
39995  .then(function(stream) {
39996  audioContext.streamNode = audioContext.createMediaStreamSource(stream);
39997  audioContext.streamNode.connect(audioWorklet);
39998  audioWorklet.connect(audioContext.destination);
39999  getUserMediaResult = 0; /* 0 = MA_SUCCESS */
40000  })
40001  .catch(function(error) {
40002  console.log("navigator.mediaDevices.getUserMedia Failed: " + error);
40003  getUserMediaResult = -1; /* -1 = MA_ERROR */
40004  });
40005 
40006  return getUserMediaResult;
40007  }, pParameters->pDevice->webaudio.audioWorklet, audioContext);
40008 
40009  if (attachmentResult != MA_SUCCESS) {
40010  ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node.");
40011  emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet);
40012  pParameters->pDevice->webaudio.initResult = attachmentResult;
40013  ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
40014  return;
40015  }
40016  }
40017 
40018  /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */
40019  if (pParameters->pConfig->deviceType == ma_device_type_playback) {
40020  ma_result attachmentResult = (ma_result)EM_ASM_INT({
40021  var audioWorklet = emscriptenGetAudioObject($0);
40022  var audioContext = emscriptenGetAudioObject($1);
40023  audioWorklet.connect(audioContext.destination);
40024  return 0; /* 0 = MA_SUCCESS */
40025  }, pParameters->pDevice->webaudio.audioWorklet, audioContext);
40026 
40027  if (attachmentResult != MA_SUCCESS) {
40028  ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node.");
40029  pParameters->pDevice->webaudio.initResult = attachmentResult;
40030  ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
40031  return;
40032  }
40033  }
40034 
40035  /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */
40036  sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext);
40037 
40038  if (pParameters->pDescriptorCapture != NULL) {
40039  pParameters->pDescriptorCapture->format = ma_format_f32;
40040  pParameters->pDescriptorCapture->channels = (ma_uint32)channels;
40041  pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate;
40042  ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels);
40043  pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames;
40044  pParameters->pDescriptorCapture->periodCount = 1;
40045  }
40046 
40047  if (pParameters->pDescriptorPlayback != NULL) {
40048  pParameters->pDescriptorPlayback->format = ma_format_f32;
40049  pParameters->pDescriptorPlayback->channels = (ma_uint32)channels;
40050  pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate;
40051  ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels);
40052  pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames;
40053  pParameters->pDescriptorPlayback->periodCount = 1;
40054  }
40055 
40056  /* At this point we're done and we can return. */
40057  ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet);
40058  pParameters->pDevice->webaudio.initResult = MA_SUCCESS;
40059  ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
40060 }
40061 
40062 static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)
40063 {
40064  ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;
40065  WebAudioWorkletProcessorCreateOptions workletProcessorOptions;
40066 
40067  MA_ASSERT(pParameters != NULL);
40068 
40069  if (success == EM_FALSE) {
40070  pParameters->pDevice->webaudio.initResult = MA_ERROR;
40071  return;
40072  }
40073 
40074  MA_ZERO_OBJECT(&workletProcessorOptions);
40075  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? */
40076 
40077  emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters);
40078 }
40079 #endif
40080 
40081 static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
40082 {
40083  if (pConfig->deviceType == ma_device_type_loopback) {
40084  return MA_DEVICE_TYPE_NOT_SUPPORTED;
40085  }
40086 
40087  /* No exclusive mode with Web Audio. */
40088  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
40089  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
40090  return MA_SHARE_MODE_NOT_SUPPORTED;
40091  }
40092 
40093  /*
40094  With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so
40095  it might be worthwhile to look into that as well.
40096  */
40097  #if defined(MA_USE_AUDIO_WORKLETS)
40098  {
40099  EmscriptenWebAudioCreateAttributes audioContextAttributes;
40100  ma_audio_worklet_thread_initialized_data* pInitParameters;
40101  void* pStackBuffer;
40102 
40103  if (pConfig->performanceProfile == ma_performance_profile_conservative) {
40104  audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK;
40105  } else {
40106  audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE;
40107  }
40108 
40109  /*
40110  In my testing, Firefox does not seem to capture audio data properly if the sample rate is set
40111  to anything other than 48K. This does not seem to be the case for other browsers. For this reason,
40112  if the device type is anything other than playback, we'll leave the sample rate as-is and let the
40113  browser pick the appropriate rate for us.
40114  */
40115  if (pConfig->deviceType == ma_device_type_playback) {
40116  audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate;
40117  } else {
40118  audioContextAttributes.sampleRate = 0;
40119  }
40120 
40121  /* 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. */
40122  pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes);
40123 
40124 
40125  /*
40126  With the context created we can now create the worklet. We can only have a single worklet per audio
40127  context which means we'll need to craft this appropriately to handle duplex devices correctly.
40128  */
40129 
40130  /*
40131  We now need to create a worker thread. This is a bit weird because we need to allocate our
40132  own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to
40133  allocate this on the heap to keep it simple.
40134  */
40135  pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks);
40136  if (pStackBuffer == NULL) {
40137  emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
40138  return MA_OUT_OF_MEMORY;
40139  }
40140 
40141  /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */
40142  pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks);
40143  if (pInitParameters == NULL) {
40144  ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);
40145  emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
40146  return MA_OUT_OF_MEMORY;
40147  }
40148 
40149  pInitParameters->pDevice = pDevice;
40150  pInitParameters->pConfig = pConfig;
40151  pInitParameters->pDescriptorPlayback = pDescriptorPlayback;
40152  pInitParameters->pDescriptorCapture = pDescriptorCapture;
40153 
40154  /*
40155  We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of
40156  the Emscripten WebAudio stuff is asynchronous.
40157  */
40158  pDevice->webaudio.initResult = MA_BUSY;
40159  {
40160  emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters);
40161  }
40162  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`. */
40163 
40164  /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */
40165  if (pDevice->webaudio.initResult != MA_SUCCESS) {
40166  ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);
40167  emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
40168  return pDevice->webaudio.initResult;
40169  }
40170 
40171  /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */
40172  pDevice->webaudio.deviceIndex = EM_ASM_INT({
40173  return miniaudio.track_device({
40174  webaudio: emscriptenGetAudioObject($0),
40175  state: 1 /* 1 = ma_device_state_stopped */
40176  });
40177  }, pDevice->webaudio.audioContext);
40178 
40179  return MA_SUCCESS;
40180  }
40181  #else
40182  {
40183  /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */
40184  ma_uint32 deviceIndex;
40185  ma_uint32 channels;
40186  ma_uint32 sampleRate;
40187  ma_uint32 periodSizeInFrames;
40188 
40189  /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */
40190  if (pConfig->deviceType == ma_device_type_capture) {
40191  channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
40192  } else {
40193  channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
40194  }
40195 
40196  /*
40197  When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's
40198  native rate. For this reason we're leaving the sample rate untouched for capture devices.
40199  */
40200  if (pConfig->deviceType == ma_device_type_playback) {
40201  sampleRate = pDescriptorPlayback->sampleRate;
40202  } else {
40203  sampleRate = 0; /* Let the browser decide when capturing. */
40204  }
40205 
40206  /* The period size needs to be a power of 2. */
40207  if (pConfig->deviceType == ma_device_type_capture) {
40208  periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile);
40209  } else {
40210  periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile);
40211  }
40212 
40213  /* We need an intermediary buffer for doing interleaving and deinterleaving. */
40214  pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks);
40215  if (pDevice->webaudio.pIntermediaryBuffer == NULL) {
40216  return MA_OUT_OF_MEMORY;
40217  }
40218 
40219  deviceIndex = EM_ASM_INT({
40220  var deviceType = $0;
40221  var channels = $1;
40222  var sampleRate = $2;
40223  var bufferSize = $3;
40224  var pIntermediaryBuffer = $4;
40225  var pDevice = $5;
40226 
40227  if (typeof(window.miniaudio) === 'undefined') {
40228  return -1; /* Context not initialized. */
40229  }
40230 
40231  var device = {};
40232 
40233  /* First thing we need is an AudioContext. */
40234  var audioContextOptions = {};
40235  if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) {
40236  audioContextOptions.sampleRate = sampleRate;
40237  }
40238 
40239  device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions);
40240  device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */
40241  device.state = window.miniaudio.device_state.stopped;
40242 
40243  /*
40244  We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we
40245  need to specify an output and configure the channel count there.
40246  */
40247  var channelCountIn = 0;
40248  var channelCountOut = channels;
40249  if (deviceType != window.miniaudio.device_type.playback) {
40250  channelCountIn = channels;
40251  }
40252 
40253  device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut);
40254 
40255  /* The node processing callback. */
40256  device.scriptNode.onaudioprocess = function(e) {
40257  if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) {
40258  device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels);
40259  }
40260 
40261  /* Do the capture side first. */
40262  if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) {
40263  /* The data must be interleaved before being processed miniaudio. */
40264  for (var iChannel = 0; iChannel < channels; iChannel += 1) {
40265  var inputBuffer = e.inputBuffer.getChannelData(iChannel);
40266  var intermediaryBuffer = device.intermediaryBufferView;
40267 
40268  for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) {
40269  intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame];
40270  }
40271  }
40272 
40273  _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
40274  }
40275 
40276  if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) {
40277  _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
40278 
40279  for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
40280  var outputBuffer = e.outputBuffer.getChannelData(iChannel);
40281  var intermediaryBuffer = device.intermediaryBufferView;
40282 
40283  for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) {
40284  outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel];
40285  }
40286  }
40287  } else {
40288  /* It's a capture-only device. Make sure the output is silenced. */
40289  for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
40290  e.outputBuffer.getChannelData(iChannel).fill(0.0);
40291  }
40292  }
40293  };
40294 
40295  /* Now we need to connect our node to the graph. */
40296  if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) {
40297  navigator.mediaDevices.getUserMedia({audio:true, video:false})
40298  .then(function(stream) {
40299  device.streamNode = device.webaudio.createMediaStreamSource(stream);
40300  device.streamNode.connect(device.scriptNode);
40301  device.scriptNode.connect(device.webaudio.destination);
40302  })
40303  .catch(function(error) {
40304  console.log("Failed to get user media: " + error);
40305  });
40306  }
40307 
40308  if (deviceType == miniaudio.device_type.playback) {
40309  device.scriptNode.connect(device.webaudio.destination);
40310  }
40311 
40312  device.pDevice = pDevice;
40313 
40314  return miniaudio.track_device(device);
40315  }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice);
40316 
40317  if (deviceIndex < 0) {
40318  return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
40319  }
40320 
40321  pDevice->webaudio.deviceIndex = deviceIndex;
40322 
40323  /* Grab the sample rate from the audio context directly. */
40324  sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
40325 
40326  if (pDescriptorCapture != NULL) {
40327  pDescriptorCapture->format = ma_format_f32;
40328  pDescriptorCapture->channels = channels;
40329  pDescriptorCapture->sampleRate = sampleRate;
40330  ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
40331  pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
40332  pDescriptorCapture->periodCount = 1;
40333  }
40334 
40335  if (pDescriptorPlayback != NULL) {
40336  pDescriptorPlayback->format = ma_format_f32;
40337  pDescriptorPlayback->channels = channels;
40338  pDescriptorPlayback->sampleRate = sampleRate;
40339  ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
40340  pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
40341  pDescriptorPlayback->periodCount = 1;
40342  }
40343 
40344  return MA_SUCCESS;
40345  }
40346  #endif
40347 }
40348 
40349 static ma_result ma_device_start__webaudio(ma_device* pDevice)
40350 {
40351  MA_ASSERT(pDevice != NULL);
40352 
40353  EM_ASM({
40354  var device = miniaudio.get_device_by_index($0);
40355  device.webaudio.resume();
40356  device.state = miniaudio.device_state.started;
40357  }, pDevice->webaudio.deviceIndex);
40358 
40359  return MA_SUCCESS;
40360 }
40361 
40362 static ma_result ma_device_stop__webaudio(ma_device* pDevice)
40363 {
40364  MA_ASSERT(pDevice != NULL);
40365 
40366  /*
40367  From the WebAudio API documentation for AudioContext.suspend():
40368 
40369  Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the
40370  destination, and then allows the system to release its claim on audio hardware.
40371 
40372  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
40373  do any kind of explicit draining.
40374  */
40375  EM_ASM({
40376  var device = miniaudio.get_device_by_index($0);
40377  device.webaudio.suspend();
40378  device.state = miniaudio.device_state.stopped;
40379  }, pDevice->webaudio.deviceIndex);
40380 
40381  ma_device__on_notification_stopped(pDevice);
40382 
40383  return MA_SUCCESS;
40384 }
40385 
40386 static ma_result ma_context_uninit__webaudio(ma_context* pContext)
40387 {
40388  MA_ASSERT(pContext != NULL);
40389  MA_ASSERT(pContext->backend == ma_backend_webaudio);
40390 
40391  (void)pContext; /* Unused. */
40392 
40393  /* Remove the global miniaudio object from window if there are no more references to it. */
40394  EM_ASM({
40395  if (typeof(window.miniaudio) !== 'undefined') {
40396  window.miniaudio.referenceCount -= 1;
40397  if (window.miniaudio.referenceCount === 0) {
40398  delete window.miniaudio;
40399  }
40400  }
40401  });
40402 
40403  return MA_SUCCESS;
40404 }
40405 
40406 static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
40407 {
40408  int resultFromJS;
40409 
40410  MA_ASSERT(pContext != NULL);
40411 
40412  (void)pConfig; /* Unused. */
40413 
40414  /* Here is where our global JavaScript object is initialized. */
40415  resultFromJS = EM_ASM_INT({
40416  if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) {
40417  return 0; /* Web Audio not supported. */
40418  }
40419 
40420  if (typeof(window.miniaudio) === 'undefined') {
40421  window.miniaudio = {
40422  referenceCount: 0
40423  };
40424 
40425  /* Device types. */
40426  window.miniaudio.device_type = {};
40427  window.miniaudio.device_type.playback = $0;
40428  window.miniaudio.device_type.capture = $1;
40429  window.miniaudio.device_type.duplex = $2;
40430 
40431  /* Device states. */
40432  window.miniaudio.device_state = {};
40433  window.miniaudio.device_state.stopped = $3;
40434  window.miniaudio.device_state.started = $4;
40435 
40436  /* Device cache for mapping devices to indexes for JavaScript/C interop. */
40437  miniaudio.devices = [];
40438 
40439  miniaudio.track_device = function(device) {
40440  /* Try inserting into a free slot first. */
40441  for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
40442  if (miniaudio.devices[iDevice] == null) {
40443  miniaudio.devices[iDevice] = device;
40444  return iDevice;
40445  }
40446  }
40447 
40448  /* Getting here means there is no empty slots in the array so we just push to the end. */
40449  miniaudio.devices.push(device);
40450  return miniaudio.devices.length - 1;
40451  };
40452 
40453  miniaudio.untrack_device_by_index = function(deviceIndex) {
40454  /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
40455  miniaudio.devices[deviceIndex] = null;
40456 
40457  /* Trim the array if possible. */
40458  while (miniaudio.devices.length > 0) {
40459  if (miniaudio.devices[miniaudio.devices.length-1] == null) {
40460  miniaudio.devices.pop();
40461  } else {
40462  break;
40463  }
40464  }
40465  };
40466 
40467  miniaudio.untrack_device = function(device) {
40468  for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
40469  if (miniaudio.devices[iDevice] == device) {
40470  return miniaudio.untrack_device_by_index(iDevice);
40471  }
40472  }
40473  };
40474 
40475  miniaudio.get_device_by_index = function(deviceIndex) {
40476  return miniaudio.devices[deviceIndex];
40477  };
40478 
40479  miniaudio.unlock_event_types = (function(){
40480  return ['touchend', 'click'];
40481  })();
40482 
40483  miniaudio.unlock = function() {
40484  for(var i = 0; i < miniaudio.devices.length; ++i) {
40485  var device = miniaudio.devices[i];
40486  if (device != null &&
40487  device.webaudio != null &&
40488  device.state === window.miniaudio.device_state.started) {
40489 
40490  device.webaudio.resume().then(() => {
40491  Module._ma_device__on_notification_unlocked(device.pDevice);
40492  },
40493  (error) => {console.error("Failed to resume audiocontext", error);
40494  });
40495  }
40496  }
40497  miniaudio.unlock_event_types.map(function(event_type) {
40498  document.removeEventListener(event_type, miniaudio.unlock, true);
40499  });
40500  };
40501 
40502  miniaudio.unlock_event_types.map(function(event_type) {
40503  document.addEventListener(event_type, miniaudio.unlock, true);
40504  });
40505  }
40506 
40507  window.miniaudio.referenceCount += 1;
40508 
40509  return 1;
40510  }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started);
40511 
40512  if (resultFromJS != 1) {
40513  return MA_FAILED_TO_INIT_BACKEND;
40514  }
40515 
40516  pCallbacks->onContextInit = ma_context_init__webaudio;
40517  pCallbacks->onContextUninit = ma_context_uninit__webaudio;
40518  pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio;
40519  pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio;
40520  pCallbacks->onDeviceInit = ma_device_init__webaudio;
40521  pCallbacks->onDeviceUninit = ma_device_uninit__webaudio;
40522  pCallbacks->onDeviceStart = ma_device_start__webaudio;
40523  pCallbacks->onDeviceStop = ma_device_stop__webaudio;
40524  pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */
40525  pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */
40526  pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */
40527 
40528  return MA_SUCCESS;
40529 }
40530 #endif /* Web Audio */
40531 
40532 
40533 
40534 static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels)
40535 {
40536  /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
40537  if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) {
40538  ma_uint32 iChannel;
40539 
40540  if (channels == 0 || channels > MA_MAX_CHANNELS) {
40541  return MA_FALSE; /* Channel count out of range. */
40542  }
40543 
40544  /* A channel cannot be present in the channel map more than once. */
40545  for (iChannel = 0; iChannel < channels; ++iChannel) {
40546  ma_uint32 jChannel;
40547  for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
40548  if (pChannelMap[iChannel] == pChannelMap[jChannel]) {
40549  return MA_FALSE;
40550  }
40551  }
40552  }
40553  }
40554 
40555  return MA_TRUE;
40556 }
40557 
40558 
40559 static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
40560 {
40561  MA_ASSERT(pContext != NULL);
40562 
40563  if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
40564  if (pContext->callbacks.onDeviceDataLoop == NULL) {
40565  return MA_TRUE;
40566  } else {
40567  return MA_FALSE;
40568  }
40569  } else {
40570  return MA_FALSE;
40571  }
40572 }
40573 
40574 
40575 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
40576 {
40577  ma_result result;
40578 
40579  MA_ASSERT(pDevice != NULL);
40580 
40581  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40582  if (pDevice->capture.format == ma_format_unknown) {
40583  pDevice->capture.format = pDevice->capture.internalFormat;
40584  }
40585  if (pDevice->capture.channels == 0) {
40586  pDevice->capture.channels = pDevice->capture.internalChannels;
40587  }
40588  if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) {
40589  MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);
40590  if (pDevice->capture.internalChannels == pDevice->capture.channels) {
40591  ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
40592  } else {
40593  if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) {
40594  ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels);
40595  } else {
40596  ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels);
40597  }
40598  }
40599  }
40600  }
40601 
40602  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40603  if (pDevice->playback.format == ma_format_unknown) {
40604  pDevice->playback.format = pDevice->playback.internalFormat;
40605  }
40606  if (pDevice->playback.channels == 0) {
40607  pDevice->playback.channels = pDevice->playback.internalChannels;
40608  }
40609  if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) {
40610  MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);
40611  if (pDevice->playback.internalChannels == pDevice->playback.channels) {
40612  ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
40613  } else {
40614  if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) {
40615  ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels);
40616  } else {
40617  ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels);
40618  }
40619  }
40620  }
40621  }
40622 
40623  if (pDevice->sampleRate == 0) {
40624  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40625  pDevice->sampleRate = pDevice->capture.internalSampleRate;
40626  } else {
40627  pDevice->sampleRate = pDevice->playback.internalSampleRate;
40628  }
40629  }
40630 
40631  /* Data converters. */
40632  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40633  /* Converting from internal device format to client format. */
40634  ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
40635  converterConfig.formatIn = pDevice->capture.internalFormat;
40636  converterConfig.channelsIn = pDevice->capture.internalChannels;
40637  converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
40638  converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap;
40639  converterConfig.formatOut = pDevice->capture.format;
40640  converterConfig.channelsOut = pDevice->capture.channels;
40641  converterConfig.sampleRateOut = pDevice->sampleRate;
40642  converterConfig.pChannelMapOut = pDevice->capture.channelMap;
40643  converterConfig.channelMixMode = pDevice->capture.channelMixMode;
40644  converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels;
40645  converterConfig.allowDynamicSampleRate = MA_FALSE;
40646  converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
40647  converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
40648  converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
40649  converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
40650 
40651  /* Make sure the old converter is uninitialized first. */
40652  if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) {
40653  ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);
40654  }
40655 
40656  result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter);
40657  if (result != MA_SUCCESS) {
40658  return result;
40659  }
40660  }
40661 
40662  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40663  /* Converting from client format to device format. */
40664  ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
40665  converterConfig.formatIn = pDevice->playback.format;
40666  converterConfig.channelsIn = pDevice->playback.channels;
40667  converterConfig.sampleRateIn = pDevice->sampleRate;
40668  converterConfig.pChannelMapIn = pDevice->playback.channelMap;
40669  converterConfig.formatOut = pDevice->playback.internalFormat;
40670  converterConfig.channelsOut = pDevice->playback.internalChannels;
40671  converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
40672  converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap;
40673  converterConfig.channelMixMode = pDevice->playback.channelMixMode;
40674  converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels;
40675  converterConfig.allowDynamicSampleRate = MA_FALSE;
40676  converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
40677  converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
40678  converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
40679  converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
40680 
40681  /* Make sure the old converter is uninitialized first. */
40682  if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) {
40683  ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);
40684  }
40685 
40686  result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter);
40687  if (result != MA_SUCCESS) {
40688  return result;
40689  }
40690  }
40691 
40692 
40693  /*
40694  If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's
40695  a couple of situations where we'll need a heap allocated cache.
40696 
40697  The first is a duplex device for backends that use a callback for data delivery. The reason
40698  this is needed is that the input stage needs to have a buffer to place the input data while it
40699  waits for the playback stage, after which the miniaudio data callback will get fired. This is
40700  not needed for backends that use a blocking API because miniaudio manages temporary buffers on
40701  the stack to achieve this.
40702 
40703  The other situation is when the data converter does not have the ability to query the number
40704  of input frames that are required in order to process a given number of output frames. When
40705  performing data conversion, it's useful if miniaudio know exactly how many frames it needs
40706  from the client in order to generate a given number of output frames. This way, only exactly
40707  the number of frames are needed to be read from the client which means no cache is necessary.
40708  On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read
40709  in fixed sized chunks and then cache any residual unused input frames, those of which will be
40710  processed at a later stage.
40711  */
40712  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40713  ma_uint64 unused;
40714 
40715  pDevice->playback.inputCacheConsumed = 0;
40716  pDevice->playback.inputCacheRemaining = 0;
40717 
40718  if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */
40719  ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */
40720  {
40721  /* We need a heap allocated cache. We want to size this based on the period size. */
40722  void* pNewInputCache;
40723  ma_uint64 newInputCacheCap;
40724  ma_uint64 newInputCacheSizeInBytes;
40725 
40726  newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames);
40727 
40728  newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
40729  if (newInputCacheSizeInBytes > MA_SIZE_MAX) {
40730  ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
40731  pDevice->playback.pInputCache = NULL;
40732  pDevice->playback.inputCacheCap = 0;
40733  return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */
40734  }
40735 
40736  pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks);
40737  if (pNewInputCache == NULL) {
40738  ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
40739  pDevice->playback.pInputCache = NULL;
40740  pDevice->playback.inputCacheCap = 0;
40741  return MA_OUT_OF_MEMORY;
40742  }
40743 
40744  pDevice->playback.pInputCache = pNewInputCache;
40745  pDevice->playback.inputCacheCap = newInputCacheCap;
40746  } else {
40747  /* 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. */
40748  ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
40749  pDevice->playback.pInputCache = NULL;
40750  pDevice->playback.inputCacheCap = 0;
40751  }
40752  }
40753 
40754  return MA_SUCCESS;
40755 }
40756 
40757 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)
40758 {
40759  ma_result result;
40760 
40761  if (pDevice == NULL) {
40762  return MA_INVALID_ARGS;
40763  }
40764 
40765  /* Capture. */
40766  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40767  if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) {
40768  return MA_INVALID_ARGS;
40769  }
40770 
40771  pDevice->capture.internalFormat = pDescriptorCapture->format;
40772  pDevice->capture.internalChannels = pDescriptorCapture->channels;
40773  pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate;
40774  MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
40775  pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
40776  pDevice->capture.internalPeriods = pDescriptorCapture->periodCount;
40777 
40778  if (pDevice->capture.internalPeriodSizeInFrames == 0) {
40779  pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate);
40780  }
40781  }
40782 
40783  /* Playback. */
40784  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40785  if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) {
40786  return MA_INVALID_ARGS;
40787  }
40788 
40789  pDevice->playback.internalFormat = pDescriptorPlayback->format;
40790  pDevice->playback.internalChannels = pDescriptorPlayback->channels;
40791  pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate;
40792  MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
40793  pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
40794  pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount;
40795 
40796  if (pDevice->playback.internalPeriodSizeInFrames == 0) {
40797  pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate);
40798  }
40799  }
40800 
40801  /*
40802  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.
40803  For loopback devices, we need to retrieve the name of the playback device.
40804  */
40805  {
40806  ma_device_info deviceInfo;
40807 
40808  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40809  result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);
40810  if (result == MA_SUCCESS) {
40811  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
40812  } else {
40813  /* We failed to retrieve the device info. Fall back to a default name. */
40814  if (pDescriptorCapture->pDeviceID == NULL) {
40815  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
40816  } else {
40817  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
40818  }
40819  }
40820  }
40821 
40822  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40823  result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
40824  if (result == MA_SUCCESS) {
40825  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
40826  } else {
40827  /* We failed to retrieve the device info. Fall back to a default name. */
40828  if (pDescriptorPlayback->pDeviceID == NULL) {
40829  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
40830  } else {
40831  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
40832  }
40833  }
40834  }
40835  }
40836 
40837  /* Update data conversion. */
40838  return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */
40839 }
40840 
40841 
40842 static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
40843 {
40844  ma_device* pDevice = (ma_device*)pData;
40845 #ifdef MA_WIN32
40846  HRESULT CoInitializeResult;
40847 #endif
40848 
40849  MA_ASSERT(pDevice != NULL);
40850 
40851 #ifdef MA_WIN32
40852  CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
40853 #endif
40854 
40855  /*
40856  When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from
40857  ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
40858  after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
40859  thread to signal an event to know when the worker thread is ready for action.
40860  */
40861  ma_device__set_state(pDevice, ma_device_state_stopped);
40862  ma_event_signal(&pDevice->stopEvent);
40863 
40864  for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
40865  ma_result startResult;
40866  ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */
40867 
40868  /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
40869  ma_event_wait(&pDevice->wakeupEvent);
40870 
40871  /* Default result code. */
40872  pDevice->workResult = MA_SUCCESS;
40873 
40874  /* If the reason for the wake up is that we are terminating, just break from the loop. */
40875  if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
40876  break;
40877  }
40878 
40879  /*
40880  Getting to this point means the device is wanting to get started. The function that has requested that the device
40881  be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
40882  in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
40883  */
40884  MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting);
40885 
40886  /* If the device has a start callback, start it now. */
40887  if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
40888  startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice);
40889  } else {
40890  startResult = MA_SUCCESS;
40891  }
40892 
40893  /*
40894  If starting was not successful we'll need to loop back to the start and wait for something
40895  to happen (pDevice->wakeupEvent).
40896  */
40897  if (startResult != MA_SUCCESS) {
40898  pDevice->workResult = startResult;
40899  ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */
40900  continue;
40901  }
40902 
40903  /* Make sure the state is set appropriately. */
40904  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. */
40905  ma_event_signal(&pDevice->startEvent);
40906 
40907  ma_device__on_notification_started(pDevice);
40908 
40909  if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
40910  pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
40911  } else {
40912  /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */
40913  ma_device_audio_thread__default_read_write(pDevice);
40914  }
40915 
40916  /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */
40917  if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
40918  stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice);
40919  } else {
40920  stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */
40921  }
40922 
40923  /*
40924  After the device has stopped, make sure an event is posted. Don't post a stopped event if
40925  stopping failed. This can happen on some backends when the underlying stream has been
40926  stopped due to the device being physically unplugged or disabled via an OS setting.
40927  */
40928  if (stopResult == MA_SUCCESS) {
40929  ma_device__on_notification_stopped(pDevice);
40930  }
40931 
40932  /* If we stopped because the device has been uninitialized, abort now. */
40933  if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
40934  break;
40935  }
40936 
40937  /* 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. */
40938  ma_device__set_state(pDevice, ma_device_state_stopped);
40939  ma_event_signal(&pDevice->stopEvent);
40940  }
40941 
40942 #ifdef MA_WIN32
40943  if (CoInitializeResult == S_OK) {
40944  ma_CoUninitialize(pDevice->pContext);
40945  }
40946 #endif
40947 
40948  return (ma_thread_result)0;
40949 }
40950 
40951 
40952 /* Helper for determining whether or not the given device is initialized. */
40953 static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
40954 {
40955  if (pDevice == NULL) {
40956  return MA_FALSE;
40957  }
40958 
40959  return ma_device_get_state(pDevice) != ma_device_state_uninitialized;
40960 }
40961 
40962 
40963 #ifdef MA_WIN32
40964 static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
40965 {
40966  /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */
40967 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
40968  if (pContext->win32.CoInitializeResult == S_OK) {
40969  ma_CoUninitialize(pContext);
40970  }
40971 
40972  #if defined(MA_WIN32_DESKTOP)
40973  ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL);
40974  ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL);
40975  #endif
40976 
40977  ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL);
40978 #else
40979  (void)pContext;
40980 #endif
40981 
40982  return MA_SUCCESS;
40983 }
40984 
40985 static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
40986 {
40987 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
40988  #if defined(MA_WIN32_DESKTOP)
40989  /* User32.dll */
40990  pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll");
40991  if (pContext->win32.hUser32DLL == NULL) {
40992  return MA_FAILED_TO_INIT_BACKEND;
40993  }
40994 
40995  pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow");
40996  pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow");
40997 
40998 
40999  /* Advapi32.dll */
41000  pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll");
41001  if (pContext->win32.hAdvapi32DLL == NULL) {
41002  return MA_FAILED_TO_INIT_BACKEND;
41003  }
41004 
41005  pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
41006  pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey");
41007  pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
41008  #endif
41009 
41010  /* Ole32.dll */
41011  pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll");
41012  if (pContext->win32.hOle32DLL == NULL) {
41013  return MA_FAILED_TO_INIT_BACKEND;
41014  }
41015 
41016  pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize");
41017  pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx");
41018  pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize");
41019  pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance");
41020  pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree");
41021  pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear");
41022  pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2");
41023 #else
41024  (void)pContext; /* Unused. */
41025 #endif
41026 
41027  pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
41028  return MA_SUCCESS;
41029 }
41030 #else
41031 static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
41032 {
41033  (void)pContext;
41034 
41035  return MA_SUCCESS;
41036 }
41037 
41038 static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
41039 {
41040  (void)pContext;
41041 
41042  return MA_SUCCESS;
41043 }
41044 #endif
41045 
41046 static ma_result ma_context_init_backend_apis(ma_context* pContext)
41047 {
41048  ma_result result;
41049 #ifdef MA_WIN32
41050  result = ma_context_init_backend_apis__win32(pContext);
41051 #else
41052  result = ma_context_init_backend_apis__nix(pContext);
41053 #endif
41054 
41055  return result;
41056 }
41057 
41058 static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
41059 {
41060  ma_result result;
41061 #ifdef MA_WIN32
41062  result = ma_context_uninit_backend_apis__win32(pContext);
41063 #else
41064  result = ma_context_uninit_backend_apis__nix(pContext);
41065 #endif
41066 
41067  return result;
41068 }
41069 
41070 
41071 /* The default capacity doesn't need to be too big. */
41072 #ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY
41073 #define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32
41074 #endif
41075 
41076 MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void)
41077 {
41079 
41080  MA_ZERO_OBJECT(&config);
41081  config.noThread = MA_FALSE;
41082  config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY;
41083  config.jobQueueFlags = 0;
41084 
41085  return config;
41086 }
41087 
41088 
41089 static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData)
41090 {
41091  ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData;
41092  MA_ASSERT(pJobThread != NULL);
41093 
41094  for (;;) {
41095  ma_result result;
41096  ma_job job;
41097 
41098  result = ma_device_job_thread_next(pJobThread, &job);
41099  if (result != MA_SUCCESS) {
41100  break;
41101  }
41102 
41103  if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
41104  break;
41105  }
41106 
41107  ma_job_process(&job);
41108  }
41109 
41110  return (ma_thread_result)0;
41111 }
41112 
41113 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)
41114 {
41115  ma_result result;
41116  ma_job_queue_config jobQueueConfig;
41117 
41118  if (pJobThread == NULL) {
41119  return MA_INVALID_ARGS;
41120  }
41121 
41122  MA_ZERO_OBJECT(pJobThread);
41123 
41124  if (pConfig == NULL) {
41125  return MA_INVALID_ARGS;
41126  }
41127 
41128 
41129  /* Initialize the job queue before the thread to ensure it's in a valid state. */
41130  jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity);
41131 
41132  result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue);
41133  if (result != MA_SUCCESS) {
41134  return result; /* Failed to initialize job queue. */
41135  }
41136 
41137 
41138  /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */
41139  if (pConfig->noThread == MA_FALSE) {
41140  result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks);
41141  if (result != MA_SUCCESS) {
41142  ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
41143  return result; /* Failed to create the job thread. */
41144  }
41145 
41146  pJobThread->_hasThread = MA_TRUE;
41147  } else {
41148  pJobThread->_hasThread = MA_FALSE;
41149  }
41150 
41151 
41152  return MA_SUCCESS;
41153 }
41154 
41155 MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks)
41156 {
41157  if (pJobThread == NULL) {
41158  return;
41159  }
41160 
41161  /* 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. */
41162  {
41163  ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);
41164  ma_device_job_thread_post(pJobThread, &job);
41165  }
41166 
41167  /* Wait for the thread to terminate naturally. */
41168  if (pJobThread->_hasThread) {
41169  ma_thread_wait(&pJobThread->thread);
41170  }
41171 
41172  /* At this point the thread should be terminated so we can safely uninitialize the job queue. */
41173  ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
41174 }
41175 
41176 MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob)
41177 {
41178  if (pJobThread == NULL || pJob == NULL) {
41179  return MA_INVALID_ARGS;
41180  }
41181 
41182  return ma_job_queue_post(&pJobThread->jobQueue, pJob);
41183 }
41184 
41185 MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob)
41186 {
41187  if (pJob == NULL) {
41188  return MA_INVALID_ARGS;
41189  }
41190 
41191  MA_ZERO_OBJECT(pJob);
41192 
41193  if (pJobThread == NULL) {
41194  return MA_INVALID_ARGS;
41195  }
41196 
41197  return ma_job_queue_next(&pJobThread->jobQueue, pJob);
41198 }
41199 
41200 
41201 
41202 MA_API ma_context_config ma_context_config_init(void)
41203 {
41204  ma_context_config config;
41205  MA_ZERO_OBJECT(&config);
41206 
41207  return config;
41208 }
41209 
41210 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
41211 {
41212  ma_result result;
41213  ma_context_config defaultConfig;
41214  ma_backend defaultBackends[ma_backend_null+1];
41215  ma_uint32 iBackend;
41216  ma_backend* pBackendsToIterate;
41217  ma_uint32 backendsToIterateCount;
41218 
41219  if (pContext == NULL) {
41220  return MA_INVALID_ARGS;
41221  }
41222 
41223  MA_ZERO_OBJECT(pContext);
41224 
41225  /* Always make sure the config is set first to ensure properties are available as soon as possible. */
41226  if (pConfig == NULL) {
41227  defaultConfig = ma_context_config_init();
41228  pConfig = &defaultConfig;
41229  }
41230 
41231  /* Allocation callbacks need to come first because they'll be passed around to other areas. */
41232  result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);
41233  if (result != MA_SUCCESS) {
41234  return result;
41235  }
41236 
41237  /* Get a lot set up first so we can start logging ASAP. */
41238  if (pConfig->pLog != NULL) {
41239  pContext->pLog = pConfig->pLog;
41240  } else {
41241  result = ma_log_init(&pContext->allocationCallbacks, &pContext->log);
41242  if (result == MA_SUCCESS) {
41243  pContext->pLog = &pContext->log;
41244  } else {
41245  pContext->pLog = NULL; /* Logging is not available. */
41246  }
41247  }
41248 
41249  pContext->threadPriority = pConfig->threadPriority;
41250  pContext->threadStackSize = pConfig->threadStackSize;
41251  pContext->pUserData = pConfig->pUserData;
41252 
41253  /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
41254  result = ma_context_init_backend_apis(pContext);
41255  if (result != MA_SUCCESS) {
41256  return result;
41257  }
41258 
41259  for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
41260  defaultBackends[iBackend] = (ma_backend)iBackend;
41261  }
41262 
41263  pBackendsToIterate = (ma_backend*)backends;
41264  backendsToIterateCount = backendCount;
41265  if (pBackendsToIterate == NULL) {
41266  pBackendsToIterate = (ma_backend*)defaultBackends;
41267  backendsToIterateCount = ma_countof(defaultBackends);
41268  }
41269 
41270  MA_ASSERT(pBackendsToIterate != NULL);
41271 
41272  for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {
41273  ma_backend backend = pBackendsToIterate[iBackend];
41274 
41275  /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */
41276  MA_ZERO_OBJECT(&pContext->callbacks);
41277 
41278  /* These backends are using the new callback system. */
41279  switch (backend) {
41280  #ifdef MA_HAS_WASAPI
41281  case ma_backend_wasapi:
41282  {
41283  pContext->callbacks.onContextInit = ma_context_init__wasapi;
41284  } break;
41285  #endif
41286  #ifdef MA_HAS_DSOUND
41287  case ma_backend_dsound:
41288  {
41289  pContext->callbacks.onContextInit = ma_context_init__dsound;
41290  } break;
41291  #endif
41292  #ifdef MA_HAS_WINMM
41293  case ma_backend_winmm:
41294  {
41295  pContext->callbacks.onContextInit = ma_context_init__winmm;
41296  } break;
41297  #endif
41298  #ifdef MA_HAS_COREAUDIO
41299  case ma_backend_coreaudio:
41300  {
41301  pContext->callbacks.onContextInit = ma_context_init__coreaudio;
41302  } break;
41303  #endif
41304  #ifdef MA_HAS_SNDIO
41305  case ma_backend_sndio:
41306  {
41307  pContext->callbacks.onContextInit = ma_context_init__sndio;
41308  } break;
41309  #endif
41310  #ifdef MA_HAS_AUDIO4
41311  case ma_backend_audio4:
41312  {
41313  pContext->callbacks.onContextInit = ma_context_init__audio4;
41314  } break;
41315  #endif
41316  #ifdef MA_HAS_OSS
41317  case ma_backend_oss:
41318  {
41319  pContext->callbacks.onContextInit = ma_context_init__oss;
41320  } break;
41321  #endif
41322  #ifdef MA_HAS_PULSEAUDIO
41323  case ma_backend_pulseaudio:
41324  {
41325  pContext->callbacks.onContextInit = ma_context_init__pulse;
41326  } break;
41327  #endif
41328  #ifdef MA_HAS_ALSA
41329  case ma_backend_alsa:
41330  {
41331  pContext->callbacks.onContextInit = ma_context_init__alsa;
41332  } break;
41333  #endif
41334  #ifdef MA_HAS_JACK
41335  case ma_backend_jack:
41336  {
41337  pContext->callbacks.onContextInit = ma_context_init__jack;
41338  } break;
41339  #endif
41340  #ifdef MA_HAS_AAUDIO
41341  case ma_backend_aaudio:
41342  {
41343  if (ma_is_backend_enabled(backend)) {
41344  pContext->callbacks.onContextInit = ma_context_init__aaudio;
41345  }
41346  } break;
41347  #endif
41348  #ifdef MA_HAS_OPENSL
41349  case ma_backend_opensl:
41350  {
41351  if (ma_is_backend_enabled(backend)) {
41352  pContext->callbacks.onContextInit = ma_context_init__opensl;
41353  }
41354  } break;
41355  #endif
41356  #ifdef MA_HAS_WEBAUDIO
41357  case ma_backend_webaudio:
41358  {
41359  pContext->callbacks.onContextInit = ma_context_init__webaudio;
41360  } break;
41361  #endif
41362  #ifdef MA_HAS_CUSTOM
41363  case ma_backend_custom:
41364  {
41365  /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */
41366  pContext->callbacks = pConfig->custom;
41367  } break;
41368  #endif
41369  #ifdef MA_HAS_NULL
41370  case ma_backend_null:
41371  {
41372  pContext->callbacks.onContextInit = ma_context_init__null;
41373  } break;
41374  #endif
41375 
41376  default: break;
41377  }
41378 
41379  if (pContext->callbacks.onContextInit != NULL) {
41380  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend));
41381  result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);
41382  } else {
41383  /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */
41384  if (backend != ma_backend_custom) {
41385  result = MA_BACKEND_NOT_ENABLED;
41386  } else {
41387  #if !defined(MA_HAS_CUSTOM)
41388  result = MA_BACKEND_NOT_ENABLED;
41389  #else
41390  result = MA_NO_BACKEND;
41391  #endif
41392  }
41393  }
41394 
41395  /* If this iteration was successful, return. */
41396  if (result == MA_SUCCESS) {
41397  result = ma_mutex_init(&pContext->deviceEnumLock);
41398  if (result != MA_SUCCESS) {
41399  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");
41400  }
41401 
41402  result = ma_mutex_init(&pContext->deviceInfoLock);
41403  if (result != MA_SUCCESS) {
41404  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");
41405  }
41406 
41407  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n");
41408  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
41409  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
41410  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
41411  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO");
41412 
41413  pContext->backend = backend;
41414  return result;
41415  } else {
41416  if (result == MA_BACKEND_NOT_ENABLED) {
41417  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend));
41418  } else {
41419  ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend));
41420  }
41421  }
41422  }
41423 
41424  /* If we get here it means an error occurred. */
41425  MA_ZERO_OBJECT(pContext); /* Safety. */
41426  return MA_NO_BACKEND;
41427 }
41428 
41429 MA_API ma_result ma_context_uninit(ma_context* pContext)
41430 {
41431  if (pContext == NULL) {
41432  return MA_INVALID_ARGS;
41433  }
41434 
41435  if (pContext->callbacks.onContextUninit != NULL) {
41436  pContext->callbacks.onContextUninit(pContext);
41437  }
41438 
41439  ma_mutex_uninit(&pContext->deviceEnumLock);
41440  ma_mutex_uninit(&pContext->deviceInfoLock);
41441  ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks);
41442  ma_context_uninit_backend_apis(pContext);
41443 
41444  if (pContext->pLog == &pContext->log) {
41445  ma_log_uninit(&pContext->log);
41446  }
41447 
41448  return MA_SUCCESS;
41449 }
41450 
41451 MA_API size_t ma_context_sizeof(void)
41452 {
41453  return sizeof(ma_context);
41454 }
41455 
41456 
41457 MA_API ma_log* ma_context_get_log(ma_context* pContext)
41458 {
41459  if (pContext == NULL) {
41460  return NULL;
41461  }
41462 
41463  return pContext->pLog;
41464 }
41465 
41466 
41467 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
41468 {
41469  ma_result result;
41470 
41471  if (pContext == NULL || callback == NULL) {
41472  return MA_INVALID_ARGS;
41473  }
41474 
41475  if (pContext->callbacks.onContextEnumerateDevices == NULL) {
41476  return MA_INVALID_OPERATION;
41477  }
41478 
41479  ma_mutex_lock(&pContext->deviceEnumLock);
41480  {
41481  result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);
41482  }
41483  ma_mutex_unlock(&pContext->deviceEnumLock);
41484 
41485  return result;
41486 }
41487 
41488 
41489 static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
41490 {
41491  /*
41492  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
41493  it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
41494  */
41495 
41496  /*
41497  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
41498  simple fixed size increment for buffer expansion.
41499  */
41500  const ma_uint32 bufferExpansionCount = 2;
41501  const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
41502 
41503  if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) {
41504  ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount;
41505  ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks);
41506  if (pNewInfos == NULL) {
41507  return MA_FALSE; /* Out of memory. */
41508  }
41509 
41510  pContext->pDeviceInfos = pNewInfos;
41511  pContext->deviceInfoCapacity = newCapacity;
41512  }
41513 
41514  if (deviceType == ma_device_type_playback) {
41515  /* Playback. Insert just before the first capture device. */
41516 
41517  /* The first thing to do is move all of the capture devices down a slot. */
41518  ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
41519  size_t iCaptureDevice;
41520  for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
41521  pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
41522  }
41523 
41524  /* Now just insert where the first capture device was before moving it down a slot. */
41525  pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
41526  pContext->playbackDeviceInfoCount += 1;
41527  } else {
41528  /* Capture. Insert at the end. */
41529  pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
41530  pContext->captureDeviceInfoCount += 1;
41531  }
41532 
41533  (void)pUserData;
41534  return MA_TRUE;
41535 }
41536 
41537 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)
41538 {
41539  ma_result result;
41540 
41541  /* Safety. */
41542  if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
41543  if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
41544  if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
41545  if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
41546 
41547  if (pContext == NULL) {
41548  return MA_INVALID_ARGS;
41549  }
41550 
41551  if (pContext->callbacks.onContextEnumerateDevices == NULL) {
41552  return MA_INVALID_OPERATION;
41553  }
41554 
41555  /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
41556  ma_mutex_lock(&pContext->deviceEnumLock);
41557  {
41558  /* Reset everything first. */
41559  pContext->playbackDeviceInfoCount = 0;
41560  pContext->captureDeviceInfoCount = 0;
41561 
41562  /* Now enumerate over available devices. */
41563  result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);
41564  if (result == MA_SUCCESS) {
41565  /* Playback devices. */
41566  if (ppPlaybackDeviceInfos != NULL) {
41567  *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
41568  }
41569  if (pPlaybackDeviceCount != NULL) {
41570  *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
41571  }
41572 
41573  /* Capture devices. */
41574  if (ppCaptureDeviceInfos != NULL) {
41575  *ppCaptureDeviceInfos = pContext->pDeviceInfos;
41576  /* Capture devices come after playback devices. */
41577  if (pContext->playbackDeviceInfoCount > 0) {
41578  /* Conditional, because NULL+0 is undefined behavior. */
41579  *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount;
41580  }
41581  }
41582  if (pCaptureDeviceCount != NULL) {
41583  *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
41584  }
41585  }
41586  }
41587  ma_mutex_unlock(&pContext->deviceEnumLock);
41588 
41589  return result;
41590 }
41591 
41592 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)
41593 {
41594  ma_result result;
41595  ma_device_info deviceInfo;
41596 
41597  /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
41598  if (pContext == NULL || pDeviceInfo == NULL) {
41599  return MA_INVALID_ARGS;
41600  }
41601 
41602  MA_ZERO_OBJECT(&deviceInfo);
41603 
41604  /* Help the backend out by copying over the device ID if we have one. */
41605  if (pDeviceID != NULL) {
41606  MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
41607  }
41608 
41609  if (pContext->callbacks.onContextGetDeviceInfo == NULL) {
41610  return MA_INVALID_OPERATION;
41611  }
41612 
41613  ma_mutex_lock(&pContext->deviceInfoLock);
41614  {
41615  result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);
41616  }
41617  ma_mutex_unlock(&pContext->deviceInfoLock);
41618 
41619  *pDeviceInfo = deviceInfo;
41620  return result;
41621 }
41622 
41623 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
41624 {
41625  if (pContext == NULL) {
41626  return MA_FALSE;
41627  }
41628 
41629  return ma_is_loopback_supported(pContext->backend);
41630 }
41631 
41632 
41633 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType)
41634 {
41635  ma_device_config config;
41636  MA_ZERO_OBJECT(&config);
41637  config.deviceType = deviceType;
41638  config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */
41639 
41640  return config;
41641 }
41642 
41643 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
41644 {
41645  ma_result result;
41646  ma_device_descriptor descriptorPlayback;
41647  ma_device_descriptor descriptorCapture;
41648 
41649  /* The context can be null, in which case we self-manage it. */
41650  if (pContext == NULL) {
41651  return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
41652  }
41653 
41654  if (pDevice == NULL) {
41655  return MA_INVALID_ARGS;
41656  }
41657 
41658  MA_ZERO_OBJECT(pDevice);
41659 
41660  if (pConfig == NULL) {
41661  return MA_INVALID_ARGS;
41662  }
41663 
41664  /* Check that we have our callbacks defined. */
41665  if (pContext->callbacks.onDeviceInit == NULL) {
41666  return MA_INVALID_OPERATION;
41667  }
41668 
41669  /* Basic config validation. */
41670  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
41671  if (pConfig->capture.channels > MA_MAX_CHANNELS) {
41672  return MA_INVALID_ARGS;
41673  }
41674 
41675  if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) {
41676  return MA_INVALID_ARGS;
41677  }
41678  }
41679 
41680  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
41681  if (pConfig->playback.channels > MA_MAX_CHANNELS) {
41682  return MA_INVALID_ARGS;
41683  }
41684 
41685  if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) {
41686  return MA_INVALID_ARGS;
41687  }
41688  }
41689 
41690  pDevice->pContext = pContext;
41691 
41692  /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
41693  pDevice->pUserData = pConfig->pUserData;
41694  pDevice->onData = pConfig->dataCallback;
41695  pDevice->onNotification = pConfig->notificationCallback;
41696  pDevice->onStop = pConfig->stopCallback;
41697 
41698  if (pConfig->playback.pDeviceID != NULL) {
41699  MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
41700  pDevice->playback.pID = &pDevice->playback.id;
41701  } else {
41702  pDevice->playback.pID = NULL;
41703  }
41704 
41705  if (pConfig->capture.pDeviceID != NULL) {
41706  MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
41707  pDevice->capture.pID = &pDevice->capture.id;
41708  } else {
41709  pDevice->capture.pID = NULL;
41710  }
41711 
41712  pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer;
41713  pDevice->noClip = pConfig->noClip;
41714  pDevice->noDisableDenormals = pConfig->noDisableDenormals;
41715  pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback;
41716  ma_atomic_float_set(&pDevice->masterVolumeFactor, 1);
41717 
41718  pDevice->type = pConfig->deviceType;
41719  pDevice->sampleRate = pConfig->sampleRate;
41720  pDevice->resampling.algorithm = pConfig->resampling.algorithm;
41721  pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
41722  pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable;
41723  pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData;
41724 
41725  pDevice->capture.shareMode = pConfig->capture.shareMode;
41726  pDevice->capture.format = pConfig->capture.format;
41727  pDevice->capture.channels = pConfig->capture.channels;
41728  ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
41729  pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
41730  pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels;
41731 
41732  pDevice->playback.shareMode = pConfig->playback.shareMode;
41733  pDevice->playback.format = pConfig->playback.format;
41734  pDevice->playback.channels = pConfig->playback.channels;
41735  ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
41736  pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
41737  pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels;
41738 
41739  result = ma_mutex_init(&pDevice->startStopLock);
41740  if (result != MA_SUCCESS) {
41741  return result;
41742  }
41743 
41744  /*
41745  When the device is started, the worker thread is the one that does the actual startup of the backend device. We
41746  use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
41747 
41748  Each of these semaphores is released internally by the worker thread when the work is completed. The start
41749  semaphore is also used to wake up the worker thread.
41750  */
41751  result = ma_event_init(&pDevice->wakeupEvent);
41752  if (result != MA_SUCCESS) {
41753  ma_mutex_uninit(&pDevice->startStopLock);
41754  return result;
41755  }
41756 
41757  result = ma_event_init(&pDevice->startEvent);
41758  if (result != MA_SUCCESS) {
41759  ma_event_uninit(&pDevice->wakeupEvent);
41760  ma_mutex_uninit(&pDevice->startStopLock);
41761  return result;
41762  }
41763 
41764  result = ma_event_init(&pDevice->stopEvent);
41765  if (result != MA_SUCCESS) {
41766  ma_event_uninit(&pDevice->startEvent);
41767  ma_event_uninit(&pDevice->wakeupEvent);
41768  ma_mutex_uninit(&pDevice->startStopLock);
41769  return result;
41770  }
41771 
41772 
41773  MA_ZERO_OBJECT(&descriptorPlayback);
41774  descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
41775  descriptorPlayback.shareMode = pConfig->playback.shareMode;
41776  descriptorPlayback.format = pConfig->playback.format;
41777  descriptorPlayback.channels = pConfig->playback.channels;
41778  descriptorPlayback.sampleRate = pConfig->sampleRate;
41779  ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
41780  descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
41781  descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
41782  descriptorPlayback.periodCount = pConfig->periods;
41783 
41784  if (descriptorPlayback.periodCount == 0) {
41785  descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
41786  }
41787 
41788 
41789  MA_ZERO_OBJECT(&descriptorCapture);
41790  descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
41791  descriptorCapture.shareMode = pConfig->capture.shareMode;
41792  descriptorCapture.format = pConfig->capture.format;
41793  descriptorCapture.channels = pConfig->capture.channels;
41794  descriptorCapture.sampleRate = pConfig->sampleRate;
41795  ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
41796  descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
41797  descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
41798  descriptorCapture.periodCount = pConfig->periods;
41799 
41800  if (descriptorCapture.periodCount == 0) {
41801  descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
41802  }
41803 
41804 
41805  result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
41806  if (result != MA_SUCCESS) {
41807  ma_event_uninit(&pDevice->startEvent);
41808  ma_event_uninit(&pDevice->wakeupEvent);
41809  ma_mutex_uninit(&pDevice->startStopLock);
41810  return result;
41811  }
41812 
41813 #if 0
41814  /*
41815  On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between
41816  the requested format and the internal format.
41817  */
41818  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
41819  if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
41820  ma_device_uninit(pDevice);
41821  return MA_INVALID_ARGS;
41822  }
41823 
41824  pDevice->capture.internalFormat = descriptorCapture.format;
41825  pDevice->capture.internalChannels = descriptorCapture.channels;
41826  pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
41827  ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
41828  pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
41829  pDevice->capture.internalPeriods = descriptorCapture.periodCount;
41830 
41831  if (pDevice->capture.internalPeriodSizeInFrames == 0) {
41832  pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate);
41833  }
41834  }
41835 
41836  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
41837  if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
41838  ma_device_uninit(pDevice);
41839  return MA_INVALID_ARGS;
41840  }
41841 
41842  pDevice->playback.internalFormat = descriptorPlayback.format;
41843  pDevice->playback.internalChannels = descriptorPlayback.channels;
41844  pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
41845  ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
41846  pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
41847  pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
41848 
41849  if (pDevice->playback.internalPeriodSizeInFrames == 0) {
41850  pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate);
41851  }
41852  }
41853 
41854 
41855  /*
41856  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.
41857  For loopback devices, we need to retrieve the name of the playback device.
41858  */
41859  {
41860  ma_device_info deviceInfo;
41861 
41862  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
41863  result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);
41864  if (result == MA_SUCCESS) {
41865  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
41866  } else {
41867  /* We failed to retrieve the device info. Fall back to a default name. */
41868  if (descriptorCapture.pDeviceID == NULL) {
41869  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
41870  } else {
41871  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
41872  }
41873  }
41874  }
41875 
41876  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
41877  result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
41878  if (result == MA_SUCCESS) {
41879  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
41880  } else {
41881  /* We failed to retrieve the device info. Fall back to a default name. */
41882  if (descriptorPlayback.pDeviceID == NULL) {
41883  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
41884  } else {
41885  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
41886  }
41887  }
41888  }
41889  }
41890 
41891 
41892  ma_device__post_init_setup(pDevice, pConfig->deviceType);
41893 #endif
41894 
41895  result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture);
41896  if (result != MA_SUCCESS) {
41897  ma_device_uninit(pDevice);
41898  return result;
41899  }
41900 
41901 
41902  /*
41903  If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to
41904  be done after post_init_setup() because we'll need access to the sample rate.
41905  */
41906  if (pConfig->noFixedSizedCallback == MA_FALSE) {
41907  /* We're using a fixed sized data callback so we'll need an intermediary buffer. */
41908  ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames;
41909  if (intermediaryBufferCap == 0) {
41910  intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate);
41911  }
41912 
41913  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
41914  ma_uint32 intermediaryBufferSizeInBytes;
41915 
41916  pDevice->capture.intermediaryBufferLen = 0;
41917  pDevice->capture.intermediaryBufferCap = intermediaryBufferCap;
41918  if (pDevice->capture.intermediaryBufferCap == 0) {
41919  pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames;
41920  }
41921 
41922  intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
41923 
41924  pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
41925  if (pDevice->capture.pIntermediaryBuffer == NULL) {
41926  ma_device_uninit(pDevice);
41927  return MA_OUT_OF_MEMORY;
41928  }
41929 
41930  /* Silence the buffer for safety. */
41931  ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels);
41932  pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap;
41933  }
41934 
41935  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
41936  ma_uint64 intermediaryBufferSizeInBytes;
41937 
41938  pDevice->playback.intermediaryBufferLen = 0;
41939  if (pConfig->deviceType == ma_device_type_duplex) {
41940  pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */
41941  } else {
41942  pDevice->playback.intermediaryBufferCap = intermediaryBufferCap;
41943  if (pDevice->playback.intermediaryBufferCap == 0) {
41944  pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames;
41945  }
41946  }
41947 
41948  intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
41949 
41950  pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
41951  if (pDevice->playback.pIntermediaryBuffer == NULL) {
41952  ma_device_uninit(pDevice);
41953  return MA_OUT_OF_MEMORY;
41954  }
41955 
41956  /* Silence the buffer for safety. */
41957  ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels);
41958  pDevice->playback.intermediaryBufferLen = 0;
41959  }
41960  } else {
41961  /* Not using a fixed sized data callback so no need for an intermediary buffer. */
41962  }
41963 
41964 
41965  /* Some backends don't require the worker thread. */
41966  if (!ma_context_is_backend_asynchronous(pContext)) {
41967  /* The worker thread. */
41968  result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks);
41969  if (result != MA_SUCCESS) {
41970  ma_device_uninit(pDevice);
41971  return result;
41972  }
41973 
41974  /* Wait for the worker thread to put the device into it's stopped state for real. */
41975  ma_event_wait(&pDevice->stopEvent);
41976  MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
41977  } else {
41978  /*
41979  If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done
41980  after ma_device__post_init_setup().
41981  */
41982  if (ma_context_is_backend_asynchronous(pContext)) {
41983  if (pConfig->deviceType == ma_device_type_duplex) {
41984  result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
41985  if (result != MA_SUCCESS) {
41986  ma_device_uninit(pDevice);
41987  return result;
41988  }
41989  }
41990  }
41991 
41992  ma_device__set_state(pDevice, ma_device_state_stopped);
41993  }
41994 
41995  /* Log device information. */
41996  {
41997  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
41998  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
41999  char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
42000  ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL);
42001 
42002  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture");
42003  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));
42004  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels);
42005  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate);
42006  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));
42007  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
42008  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
42009  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
42010  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
42011  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
42012  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
42013  {
42014  char channelMapStr[1024];
42015  ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr));
42016  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr);
42017 
42018  ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr));
42019  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr);
42020  }
42021  }
42022  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
42023  char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
42024  ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL);
42025 
42026  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback");
42027  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));
42028  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
42029  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
42030  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));
42031  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
42032  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
42033  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
42034  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
42035  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
42036  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
42037  {
42038  char channelMapStr[1024];
42039  ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr));
42040  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr);
42041 
42042  ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr));
42043  ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr);
42044  }
42045  }
42046  }
42047 
42048  MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
42049  return MA_SUCCESS;
42050 }
42051 
42052 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)
42053 {
42054  ma_result result;
42055  ma_context* pContext;
42056  ma_backend defaultBackends[ma_backend_null+1];
42057  ma_uint32 iBackend;
42058  ma_backend* pBackendsToIterate;
42059  ma_uint32 backendsToIterateCount;
42060  ma_allocation_callbacks allocationCallbacks;
42061 
42062  if (pConfig == NULL) {
42063  return MA_INVALID_ARGS;
42064  }
42065 
42066  if (pContextConfig != NULL) {
42067  result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
42068  if (result != MA_SUCCESS) {
42069  return result;
42070  }
42071  } else {
42072  allocationCallbacks = ma_allocation_callbacks_init_default();
42073  }
42074 
42075  pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks);
42076  if (pContext == NULL) {
42077  return MA_OUT_OF_MEMORY;
42078  }
42079 
42080  for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
42081  defaultBackends[iBackend] = (ma_backend)iBackend;
42082  }
42083 
42084  pBackendsToIterate = (ma_backend*)backends;
42085  backendsToIterateCount = backendCount;
42086  if (pBackendsToIterate == NULL) {
42087  pBackendsToIterate = (ma_backend*)defaultBackends;
42088  backendsToIterateCount = ma_countof(defaultBackends);
42089  }
42090 
42091  result = MA_NO_BACKEND;
42092 
42093  for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
42094  /*
42095  This is a hack for iOS. If the context config is null, there's a good chance the
42096  `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this
42097  case, set the session category based on the device type.
42098  */
42099  #if defined(MA_APPLE_MOBILE)
42100  ma_context_config contextConfig;
42101 
42102  if (pContextConfig == NULL) {
42103  contextConfig = ma_context_config_init();
42104  switch (pConfig->deviceType) {
42105  case ma_device_type_duplex: {
42106  contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record;
42107  } break;
42108  case ma_device_type_capture: {
42109  contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record;
42110  } break;
42111  case ma_device_type_playback:
42112  default: {
42113  contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback;
42114  } break;
42115  }
42116 
42117  pContextConfig = &contextConfig;
42118  }
42119  #endif
42120 
42121  result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
42122  if (result == MA_SUCCESS) {
42123  result = ma_device_init(pContext, pConfig, pDevice);
42124  if (result == MA_SUCCESS) {
42125  break; /* Success. */
42126  } else {
42127  ma_context_uninit(pContext); /* Failure. */
42128  }
42129  }
42130  }
42131 
42132  if (result != MA_SUCCESS) {
42133  ma_free(pContext, &allocationCallbacks);
42134  return result;
42135  }
42136 
42137  pDevice->isOwnerOfContext = MA_TRUE;
42138  return result;
42139 }
42140 
42141 MA_API void ma_device_uninit(ma_device* pDevice)
42142 {
42143  if (!ma_device__is_initialized(pDevice)) {
42144  return;
42145  }
42146 
42147  /*
42148  It's possible for the miniaudio side of the device and the backend to not be in sync due to
42149  system-level situations such as the computer being put into sleep mode and the backend not
42150  notifying miniaudio of the fact the device has stopped. It's possible for this to result in a
42151  deadlock due to miniaudio thinking the device is in a running state, when in fact it's not
42152  running at all. For this reason I am no longer explicitly stopping the device. I don't think
42153  this should affect anyone in practice since uninitializing the backend will naturally stop the
42154  device anyway.
42155  */
42156  #if 0
42157  {
42158  /* 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. */
42159  if (ma_device_is_started(pDevice)) {
42160  ma_device_stop(pDevice);
42161  }
42162  }
42163  #endif
42164 
42165  /* Putting the device into an uninitialized state will make the worker thread return. */
42166  ma_device__set_state(pDevice, ma_device_state_uninitialized);
42167 
42168  /* Wake up the worker thread and wait for it to properly terminate. */
42169  if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
42170  ma_event_signal(&pDevice->wakeupEvent);
42171  ma_thread_wait(&pDevice->thread);
42172  }
42173 
42174  if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
42175  pDevice->pContext->callbacks.onDeviceUninit(pDevice);
42176  }
42177 
42178 
42179  ma_event_uninit(&pDevice->stopEvent);
42180  ma_event_uninit(&pDevice->startEvent);
42181  ma_event_uninit(&pDevice->wakeupEvent);
42182  ma_mutex_uninit(&pDevice->startStopLock);
42183 
42184  if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
42185  if (pDevice->type == ma_device_type_duplex) {
42186  ma_duplex_rb_uninit(&pDevice->duplexRB);
42187  }
42188  }
42189 
42190  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
42191  ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);
42192  }
42193  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
42194  ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);
42195  }
42196 
42197  if (pDevice->playback.pInputCache != NULL) {
42198  ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
42199  }
42200 
42201  if (pDevice->capture.pIntermediaryBuffer != NULL) {
42202  ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
42203  }
42204  if (pDevice->playback.pIntermediaryBuffer != NULL) {
42205  ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
42206  }
42207 
42208  if (pDevice->isOwnerOfContext) {
42209  ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
42210 
42211  ma_context_uninit(pDevice->pContext);
42212  ma_free(pDevice->pContext, &allocationCallbacks);
42213  }
42214 
42215  MA_ZERO_OBJECT(pDevice);
42216 }
42217 
42218 MA_API ma_context* ma_device_get_context(ma_device* pDevice)
42219 {
42220  if (pDevice == NULL) {
42221  return NULL;
42222  }
42223 
42224  return pDevice->pContext;
42225 }
42226 
42227 MA_API ma_log* ma_device_get_log(ma_device* pDevice)
42228 {
42229  return ma_context_get_log(ma_device_get_context(pDevice));
42230 }
42231 
42232 MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
42233 {
42234  if (pDeviceInfo == NULL) {
42235  return MA_INVALID_ARGS;
42236  }
42237 
42238  MA_ZERO_OBJECT(pDeviceInfo);
42239 
42240  if (pDevice == NULL) {
42241  return MA_INVALID_ARGS;
42242  }
42243 
42244  /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */
42245  if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) {
42246  return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo);
42247  }
42248 
42249  /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */
42250  if (type == ma_device_type_playback) {
42251  return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo);
42252  } else {
42253  return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo);
42254  }
42255 }
42256 
42257 MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator)
42258 {
42259  ma_result result;
42260  ma_device_info deviceInfo;
42261 
42262  if (pLengthNotIncludingNullTerminator != NULL) {
42263  *pLengthNotIncludingNullTerminator = 0;
42264  }
42265 
42266  if (pName != NULL && nameCap > 0) {
42267  pName[0] = '\0';
42268  }
42269 
42270  result = ma_device_get_info(pDevice, type, &deviceInfo);
42271  if (result != MA_SUCCESS) {
42272  return result;
42273  }
42274 
42275  if (pName != NULL) {
42276  ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1);
42277 
42278  /*
42279  For safety, make sure the length is based on the truncated output string rather than the
42280  source. Otherwise the caller might assume the output buffer contains more content than it
42281  actually does.
42282  */
42283  if (pLengthNotIncludingNullTerminator != NULL) {
42284  *pLengthNotIncludingNullTerminator = strlen(pName);
42285  }
42286  } else {
42287  /* Name not specified. Just report the length of the source string. */
42288  if (pLengthNotIncludingNullTerminator != NULL) {
42289  *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name);
42290  }
42291  }
42292 
42293  return MA_SUCCESS;
42294 }
42295 
42296 MA_API ma_result ma_device_start(ma_device* pDevice)
42297 {
42298  ma_result result;
42299 
42300  if (pDevice == NULL) {
42301  return MA_INVALID_ARGS;
42302  }
42303 
42304  if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
42305  return MA_INVALID_OPERATION; /* Not initialized. */
42306  }
42307 
42308  if (ma_device_get_state(pDevice) == ma_device_state_started) {
42309  return MA_SUCCESS; /* Already started. */
42310  }
42311 
42312  ma_mutex_lock(&pDevice->startStopLock);
42313  {
42314  /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
42315  MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
42316 
42317  ma_device__set_state(pDevice, ma_device_state_starting);
42318 
42319  /* Asynchronous backends need to be handled differently. */
42320  if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
42321  if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
42322  result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
42323  } else {
42324  result = MA_INVALID_OPERATION;
42325  }
42326 
42327  if (result == MA_SUCCESS) {
42328  ma_device__set_state(pDevice, ma_device_state_started);
42329  ma_device__on_notification_started(pDevice);
42330  }
42331  } else {
42332  /*
42333  Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
42334  thread and then wait for the start event.
42335  */
42336  ma_event_signal(&pDevice->wakeupEvent);
42337 
42338  /*
42339  Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
42340  into the started state. Don't call ma_device__set_state() here.
42341  */
42342  ma_event_wait(&pDevice->startEvent);
42343  result = pDevice->workResult;
42344  }
42345 
42346  /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */
42347  if (result != MA_SUCCESS) {
42348  ma_device__set_state(pDevice, ma_device_state_stopped);
42349  }
42350  }
42351  ma_mutex_unlock(&pDevice->startStopLock);
42352 
42353  return result;
42354 }
42355 
42356 MA_API ma_result ma_device_stop(ma_device* pDevice)
42357 {
42358  ma_result result;
42359 
42360  if (pDevice == NULL) {
42361  return MA_INVALID_ARGS;
42362  }
42363 
42364  if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
42365  return MA_INVALID_OPERATION; /* Not initialized. */
42366  }
42367 
42368  if (ma_device_get_state(pDevice) == ma_device_state_stopped) {
42369  return MA_SUCCESS; /* Already stopped. */
42370  }
42371 
42372  ma_mutex_lock(&pDevice->startStopLock);
42373  {
42374  /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
42375  MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
42376 
42377  ma_device__set_state(pDevice, ma_device_state_stopping);
42378 
42379  /* Asynchronous backends need to be handled differently. */
42380  if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
42381  /* Asynchronous backends must have a stop operation. */
42382  if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
42383  result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
42384  } else {
42385  result = MA_INVALID_OPERATION;
42386  }
42387 
42388  ma_device__set_state(pDevice, ma_device_state_stopped);
42389  } else {
42390  /*
42391  Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
42392  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
42393  sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
42394  important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
42395  */
42396  MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);
42397 
42398  if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
42399  pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
42400  }
42401 
42402  /*
42403  We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
42404  the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
42405  */
42406  ma_event_wait(&pDevice->stopEvent);
42407  result = MA_SUCCESS;
42408  }
42409 
42410  /*
42411  This is a safety measure to ensure the internal buffer has been cleared so any leftover
42412  does not get played the next time the device starts. Ideally this should be drained by
42413  the backend first.
42414  */
42415  pDevice->playback.intermediaryBufferLen = 0;
42416  pDevice->playback.inputCacheConsumed = 0;
42417  pDevice->playback.inputCacheRemaining = 0;
42418  }
42419  ma_mutex_unlock(&pDevice->startStopLock);
42420 
42421  return result;
42422 }
42423 
42424 MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice)
42425 {
42426  return ma_device_get_state(pDevice) == ma_device_state_started;
42427 }
42428 
42429 MA_API ma_device_state ma_device_get_state(const ma_device* pDevice)
42430 {
42431  if (pDevice == NULL) {
42432  return ma_device_state_uninitialized;
42433  }
42434 
42435  return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */
42436 }
42437 
42438 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
42439 {
42440  if (pDevice == NULL) {
42441  return MA_INVALID_ARGS;
42442  }
42443 
42444  if (volume < 0.0f) {
42445  return MA_INVALID_ARGS;
42446  }
42447 
42448  ma_atomic_float_set(&pDevice->masterVolumeFactor, volume);
42449 
42450  return MA_SUCCESS;
42451 }
42452 
42453 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
42454 {
42455  if (pVolume == NULL) {
42456  return MA_INVALID_ARGS;
42457  }
42458 
42459  if (pDevice == NULL) {
42460  *pVolume = 0;
42461  return MA_INVALID_ARGS;
42462  }
42463 
42464  *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor);
42465 
42466  return MA_SUCCESS;
42467 }
42468 
42469 MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB)
42470 {
42471  if (gainDB > 0) {
42472  return MA_INVALID_ARGS;
42473  }
42474 
42475  return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB));
42476 }
42477 
42478 MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB)
42479 {
42480  float factor;
42481  ma_result result;
42482 
42483  if (pGainDB == NULL) {
42484  return MA_INVALID_ARGS;
42485  }
42486 
42487  result = ma_device_get_master_volume(pDevice, &factor);
42488  if (result != MA_SUCCESS) {
42489  *pGainDB = 0;
42490  return result;
42491  }
42492 
42493  *pGainDB = ma_volume_linear_to_db(factor);
42494 
42495  return MA_SUCCESS;
42496 }
42497 
42498 
42499 MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
42500 {
42501  if (pDevice == NULL) {
42502  return MA_INVALID_ARGS;
42503  }
42504 
42505  if (pOutput == NULL && pInput == NULL) {
42506  return MA_INVALID_ARGS;
42507  }
42508 
42509  if (pDevice->type == ma_device_type_duplex) {
42510  if (pInput != NULL) {
42511  ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
42512  }
42513 
42514  if (pOutput != NULL) {
42515  ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);
42516  }
42517  } else {
42518  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {
42519  if (pInput == NULL) {
42520  return MA_INVALID_ARGS;
42521  }
42522 
42523  ma_device__send_frames_to_client(pDevice, frameCount, pInput);
42524  }
42525 
42526  if (pDevice->type == ma_device_type_playback) {
42527  if (pOutput == NULL) {
42528  return MA_INVALID_ARGS;
42529  }
42530 
42531  ma_device__read_frames_from_client(pDevice, frameCount, pOutput);
42532  }
42533  }
42534 
42535  return MA_SUCCESS;
42536 }
42537 
42538 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
42539 {
42540  if (pDescriptor == NULL) {
42541  return 0;
42542  }
42543 
42544  /*
42545  We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the
42546  time when the size of the buffer needs to be determined. In this case we need to just take a best
42547  guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll
42548  just fall back to MA_DEFAULT_SAMPLE_RATE.
42549  */
42550  if (nativeSampleRate == 0) {
42551  nativeSampleRate = pDescriptor->sampleRate;
42552  }
42553  if (nativeSampleRate == 0) {
42554  nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
42555  }
42556 
42557  MA_ASSERT(nativeSampleRate != 0);
42558 
42559  if (pDescriptor->periodSizeInFrames == 0) {
42560  if (pDescriptor->periodSizeInMilliseconds == 0) {
42561  if (performanceProfile == ma_performance_profile_low_latency) {
42562  return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);
42563  } else {
42564  return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);
42565  }
42566  } else {
42567  return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
42568  }
42569  } else {
42570  return pDescriptor->periodSizeInFrames;
42571  }
42572 }
42573 #endif /* MA_NO_DEVICE_IO */
42574 
42575 
42576 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
42577 {
42578  /* Prevent a division by zero. */
42579  if (sampleRate == 0) {
42580  return 0;
42581  }
42582 
42583  return bufferSizeInFrames*1000 / sampleRate;
42584 }
42585 
42586 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
42587 {
42588  /* Prevent a division by zero. */
42589  if (sampleRate == 0) {
42590  return 0;
42591  }
42592 
42593  return bufferSizeInMilliseconds*sampleRate / 1000;
42594 }
42595 
42596 MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
42597 {
42598  if (dst == src) {
42599  return; /* No-op. */
42600  }
42601 
42602  ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));
42603 }
42604 
42605 MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
42606 {
42607  if (format == ma_format_u8) {
42608  ma_uint64 sampleCount = frameCount * channels;
42609  ma_uint64 iSample;
42610  for (iSample = 0; iSample < sampleCount; iSample += 1) {
42611  ((ma_uint8*)p)[iSample] = 128;
42612  }
42613  } else {
42614  ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels));
42615  }
42616 }
42617 
42618 MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
42619 {
42620  return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
42621 }
42622 
42623 MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
42624 {
42625  return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
42626 }
42627 
42628 
42629 MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count)
42630 {
42631  ma_uint64 iSample;
42632 
42633  MA_ASSERT(pDst != NULL);
42634  MA_ASSERT(pSrc != NULL);
42635 
42636  for (iSample = 0; iSample < count; iSample += 1) {
42637  pDst[iSample] = ma_clip_u8(pSrc[iSample]);
42638  }
42639 }
42640 
42641 MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* 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_s16(pSrc[iSample]);
42650  }
42651 }
42652 
42653 MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* 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  ma_int64 s = ma_clip_s24(pSrc[iSample]);
42662  pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
42663  pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
42664  pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
42665  }
42666 }
42667 
42668 MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count)
42669 {
42670  ma_uint64 iSample;
42671 
42672  MA_ASSERT(pDst != NULL);
42673  MA_ASSERT(pSrc != NULL);
42674 
42675  for (iSample = 0; iSample < count; iSample += 1) {
42676  pDst[iSample] = ma_clip_s32(pSrc[iSample]);
42677  }
42678 }
42679 
42680 MA_API void ma_clip_samples_f32(float* pDst, const float* 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_f32(pSrc[iSample]);
42689  }
42690 }
42691 
42692 MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
42693 {
42694  ma_uint64 sampleCount;
42695 
42696  MA_ASSERT(pDst != NULL);
42697  MA_ASSERT(pSrc != NULL);
42698 
42699  sampleCount = frameCount * channels;
42700 
42701  switch (format) {
42702  case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break;
42703  case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break;
42704  case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break;
42705  case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break;
42706  case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break;
42707 
42708  /* 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. */
42709  case ma_format_unknown:
42710  case ma_format_count:
42711  break;
42712  }
42713 }
42714 
42715 
42716 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)
42717 {
42718  ma_uint64 iSample;
42719 
42720  if (pSamplesOut == NULL || pSamplesIn == NULL) {
42721  return;
42722  }
42723 
42724  for (iSample = 0; iSample < sampleCount; iSample += 1) {
42725  pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
42726  }
42727 }
42728 
42729 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)
42730 {
42731  ma_uint64 iSample;
42732 
42733  if (pSamplesOut == NULL || pSamplesIn == NULL) {
42734  return;
42735  }
42736 
42737  for (iSample = 0; iSample < sampleCount; iSample += 1) {
42738  pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
42739  }
42740 }
42741 
42742 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)
42743 {
42744  ma_uint64 iSample;
42745  ma_uint8* pSamplesOut8;
42746  ma_uint8* pSamplesIn8;
42747 
42748  if (pSamplesOut == NULL || pSamplesIn == NULL) {
42749  return;
42750  }
42751 
42752  pSamplesOut8 = (ma_uint8*)pSamplesOut;
42753  pSamplesIn8 = (ma_uint8*)pSamplesIn;
42754 
42755  for (iSample = 0; iSample < sampleCount; iSample += 1) {
42756  ma_int32 sampleS32;
42757 
42758  sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
42759  sampleS32 = (ma_int32)(sampleS32 * factor);
42760 
42761  pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
42762  pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
42763  pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
42764  }
42765 }
42766 
42767 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)
42768 {
42769  ma_uint64 iSample;
42770 
42771  if (pSamplesOut == NULL || pSamplesIn == NULL) {
42772  return;
42773  }
42774 
42775  for (iSample = 0; iSample < sampleCount; iSample += 1) {
42776  pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
42777  }
42778 }
42779 
42780 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)
42781 {
42782  ma_uint64 iSample;
42783 
42784  if (pSamplesOut == NULL || pSamplesIn == NULL) {
42785  return;
42786  }
42787 
42788  if (factor == 1) {
42789  if (pSamplesOut == pSamplesIn) {
42790  /* In place. No-op. */
42791  } else {
42792  /* Just a copy. */
42793  for (iSample = 0; iSample < sampleCount; iSample += 1) {
42794  pSamplesOut[iSample] = pSamplesIn[iSample];
42795  }
42796  }
42797  } else {
42798  for (iSample = 0; iSample < sampleCount; iSample += 1) {
42799  pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
42800  }
42801  }
42802 }
42803 
42804 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)
42805 {
42806  ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
42807 }
42808 
42809 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)
42810 {
42811  ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
42812 }
42813 
42814 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)
42815 {
42816  ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
42817 }
42818 
42819 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)
42820 {
42821  ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
42822 }
42823 
42824 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)
42825 {
42826  ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
42827 }
42828 
42829 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)
42830 {
42831  ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor);
42832 }
42833 
42834 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)
42835 {
42836  ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor);
42837 }
42838 
42839 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)
42840 {
42841  ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor);
42842 }
42843 
42844 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)
42845 {
42846  ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor);
42847 }
42848 
42849 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)
42850 {
42851  ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor);
42852 }
42853 
42854 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)
42855 {
42856  switch (format)
42857  {
42858  case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return;
42859  case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return;
42860  case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return;
42861  case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return;
42862  case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return;
42863  default: return; /* Do nothing. */
42864  }
42865 }
42866 
42867 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42868 {
42869  ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor);
42870 }
42871 
42872 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42873 {
42874  ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor);
42875 }
42876 
42877 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42878 {
42879  ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor);
42880 }
42881 
42882 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42883 {
42884  ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor);
42885 }
42886 
42887 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42888 {
42889  ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor);
42890 }
42891 
42892 MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
42893 {
42894  ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor);
42895 }
42896 
42897 
42898 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)
42899 {
42900  ma_uint64 iFrame;
42901 
42902  if (channels == 2) {
42903  /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */
42904  }
42905 
42906  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42907  ma_uint32 iChannel;
42908  for (iChannel = 0; iChannel < channels; iChannel += 1) {
42909  pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel];
42910  }
42911  }
42912 }
42913 
42914 
42915 
42916 static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume)
42917 {
42918  return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8);
42919 }
42920 
42921 static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume)
42922 {
42923  return (ma_int32)((x * volume) >> 8);
42924 }
42925 
42926 static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume)
42927 {
42928  return (ma_int64)((x * volume) >> 8);
42929 }
42930 
42931 static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume)
42932 {
42933  return (ma_int64)((x * volume) >> 8);
42934 }
42935 
42936 static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume)
42937 {
42938  return x * volume;
42939 }
42940 
42941 
42942 MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume)
42943 {
42944  ma_uint64 iSample;
42945  ma_int16 volumeFixed;
42946 
42947  MA_ASSERT(pDst != NULL);
42948  MA_ASSERT(pSrc != NULL);
42949 
42950  volumeFixed = ma_float_to_fixed_16(volume);
42951 
42952  for (iSample = 0; iSample < count; iSample += 1) {
42953  pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed));
42954  }
42955 }
42956 
42957 MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume)
42958 {
42959  ma_uint64 iSample;
42960  ma_int16 volumeFixed;
42961 
42962  MA_ASSERT(pDst != NULL);
42963  MA_ASSERT(pSrc != NULL);
42964 
42965  volumeFixed = ma_float_to_fixed_16(volume);
42966 
42967  for (iSample = 0; iSample < count; iSample += 1) {
42968  pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed));
42969  }
42970 }
42971 
42972 MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
42973 {
42974  ma_uint64 iSample;
42975  ma_int16 volumeFixed;
42976 
42977  MA_ASSERT(pDst != NULL);
42978  MA_ASSERT(pSrc != NULL);
42979 
42980  volumeFixed = ma_float_to_fixed_16(volume);
42981 
42982  for (iSample = 0; iSample < count; iSample += 1) {
42983  ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed));
42984  pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
42985  pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
42986  pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
42987  }
42988 }
42989 
42990 MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
42991 {
42992  ma_uint64 iSample;
42993  ma_int16 volumeFixed;
42994 
42995  MA_ASSERT(pDst != NULL);
42996  MA_ASSERT(pSrc != NULL);
42997 
42998  volumeFixed = ma_float_to_fixed_16(volume);
42999 
43000  for (iSample = 0; iSample < count; iSample += 1) {
43001  pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed));
43002  }
43003 }
43004 
43005 MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume)
43006 {
43007  ma_uint64 iSample;
43008 
43009  MA_ASSERT(pDst != NULL);
43010  MA_ASSERT(pSrc != NULL);
43011 
43012  /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */
43013 
43014  for (iSample = 0; iSample < count; iSample += 1) {
43015  pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume));
43016  }
43017 }
43018 
43019 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)
43020 {
43021  MA_ASSERT(pDst != NULL);
43022  MA_ASSERT(pSrc != NULL);
43023 
43024  if (volume == 1) {
43025  ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */
43026  } else if (volume == 0) {
43027  ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */
43028  } else {
43029  ma_uint64 sampleCount = frameCount * channels;
43030 
43031  switch (format) {
43032  case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break;
43033  case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break;
43034  case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
43035  case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
43036  case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break;
43037 
43038  /* 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. */
43039  case ma_format_unknown:
43040  case ma_format_count:
43041  break;
43042  }
43043  }
43044 }
43045 
43046 
43047 
43048 MA_API float ma_volume_linear_to_db(float factor)
43049 {
43050  return 20*ma_log10f(factor);
43051 }
43052 
43053 MA_API float ma_volume_db_to_linear(float gain)
43054 {
43055  return ma_powf(10, gain/20.0f);
43056 }
43057 
43058 
43059 MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
43060 {
43061  ma_uint64 iSample;
43062  ma_uint64 sampleCount;
43063 
43064  if (pDst == NULL || pSrc == NULL || channels == 0) {
43065  return MA_INVALID_ARGS;
43066  }
43067 
43068  if (volume == 0) {
43069  return MA_SUCCESS; /* No changes if the volume is 0. */
43070  }
43071 
43072  sampleCount = frameCount * channels;
43073 
43074  if (volume == 1) {
43075  for (iSample = 0; iSample < sampleCount; iSample += 1) {
43076  pDst[iSample] += pSrc[iSample];
43077  }
43078  } else {
43079  for (iSample = 0; iSample < sampleCount; iSample += 1) {
43080  pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);
43081  }
43082  }
43083 
43084  return MA_SUCCESS;
43085 }
43086 
43087 
43088 
43089 /**************************************************************************************************************************************************************
43090 
43091 Format Conversion
43092 
43093 **************************************************************************************************************************************************************/
43094 
43095 static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)
43096 {
43097  return (ma_int16)(x * 32767.0f);
43098 }
43099 
43100 static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)
43101 {
43102  return (ma_int16)((ma_int16)x - 128);
43103 }
43104 
43105 static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x)
43106 {
43107  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. */
43108 }
43109 
43110 static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24)
43111 {
43112  s24[0] = (ma_uint8)((x & 0x000000FF) >> 0);
43113  s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8);
43114  s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16);
43115 }
43116 
43117 
43118 /* u8 */
43119 MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43120 {
43121  (void)ditherMode;
43122  ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
43123 }
43124 
43125 
43126 static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43127 {
43128  ma_int16* dst_s16 = (ma_int16*)dst;
43129  const ma_uint8* src_u8 = (const ma_uint8*)src;
43130 
43131  ma_uint64 i;
43132  for (i = 0; i < count; i += 1) {
43133  ma_int16 x = src_u8[i];
43134  x = (ma_int16)(x - 128);
43135  x = (ma_int16)(x << 8);
43136  dst_s16[i] = x;
43137  }
43138 
43139  (void)ditherMode;
43140 }
43141 
43142 static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43143 {
43144  ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
43145 }
43146 
43147 #if defined(MA_SUPPORT_SSE2)
43148 static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43149 {
43150  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
43151 }
43152 #endif
43153 #if defined(MA_SUPPORT_NEON)
43154 static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43155 {
43156  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
43157 }
43158 #endif
43159 
43160 MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43161 {
43162 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43163  ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
43164 #else
43165  # if defined(MA_SUPPORT_SSE2)
43166  if (ma_has_sse2()) {
43167  ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
43168  } else
43169  #elif defined(MA_SUPPORT_NEON)
43170  if (ma_has_neon()) {
43171  ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
43172  } else
43173  #endif
43174  {
43175  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
43176  }
43177 #endif
43178 }
43179 
43180 
43181 static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43182 {
43183  ma_uint8* dst_s24 = (ma_uint8*)dst;
43184  const ma_uint8* src_u8 = (const ma_uint8*)src;
43185 
43186  ma_uint64 i;
43187  for (i = 0; i < count; i += 1) {
43188  ma_int16 x = src_u8[i];
43189  x = (ma_int16)(x - 128);
43190 
43191  dst_s24[i*3+0] = 0;
43192  dst_s24[i*3+1] = 0;
43193  dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
43194  }
43195 
43196  (void)ditherMode;
43197 }
43198 
43199 static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43200 {
43201  ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
43202 }
43203 
43204 #if defined(MA_SUPPORT_SSE2)
43205 static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43206 {
43207  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
43208 }
43209 #endif
43210 #if defined(MA_SUPPORT_NEON)
43211 static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43212 {
43213  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
43214 }
43215 #endif
43216 
43217 MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43218 {
43219 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43220  ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
43221 #else
43222  # if defined(MA_SUPPORT_SSE2)
43223  if (ma_has_sse2()) {
43224  ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
43225  } else
43226  #elif defined(MA_SUPPORT_NEON)
43227  if (ma_has_neon()) {
43228  ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
43229  } else
43230  #endif
43231  {
43232  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
43233  }
43234 #endif
43235 }
43236 
43237 
43238 static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43239 {
43240  ma_int32* dst_s32 = (ma_int32*)dst;
43241  const ma_uint8* src_u8 = (const ma_uint8*)src;
43242 
43243  ma_uint64 i;
43244  for (i = 0; i < count; i += 1) {
43245  ma_int32 x = src_u8[i];
43246  x = x - 128;
43247  x = x << 24;
43248  dst_s32[i] = x;
43249  }
43250 
43251  (void)ditherMode;
43252 }
43253 
43254 static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43255 {
43256  ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
43257 }
43258 
43259 #if defined(MA_SUPPORT_SSE2)
43260 static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43261 {
43262  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
43263 }
43264 #endif
43265 #if defined(MA_SUPPORT_NEON)
43266 static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43267 {
43268  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
43269 }
43270 #endif
43271 
43272 MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43273 {
43274 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43275  ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
43276 #else
43277  # if defined(MA_SUPPORT_SSE2)
43278  if (ma_has_sse2()) {
43279  ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
43280  } else
43281  #elif defined(MA_SUPPORT_NEON)
43282  if (ma_has_neon()) {
43283  ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
43284  } else
43285  #endif
43286  {
43287  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
43288  }
43289 #endif
43290 }
43291 
43292 
43293 static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43294 {
43295  float* dst_f32 = (float*)dst;
43296  const ma_uint8* src_u8 = (const ma_uint8*)src;
43297 
43298  ma_uint64 i;
43299  for (i = 0; i < count; i += 1) {
43300  float x = (float)src_u8[i];
43301  x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
43302  x = x - 1; /* 0..2 to -1..1 */
43303 
43304  dst_f32[i] = x;
43305  }
43306 
43307  (void)ditherMode;
43308 }
43309 
43310 static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43311 {
43312  ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
43313 }
43314 
43315 #if defined(MA_SUPPORT_SSE2)
43316 static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43317 {
43318  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
43319 }
43320 #endif
43321 #if defined(MA_SUPPORT_NEON)
43322 static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43323 {
43324  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
43325 }
43326 #endif
43327 
43328 MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43329 {
43330 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43331  ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
43332 #else
43333  # if defined(MA_SUPPORT_SSE2)
43334  if (ma_has_sse2()) {
43335  ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
43336  } else
43337  #elif defined(MA_SUPPORT_NEON)
43338  if (ma_has_neon()) {
43339  ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
43340  } else
43341  #endif
43342  {
43343  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
43344  }
43345 #endif
43346 }
43347 
43348 
43349 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43350 static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43351 {
43352  ma_uint8* dst_u8 = (ma_uint8*)dst;
43353  const ma_uint8** src_u8 = (const ma_uint8**)src;
43354 
43355  ma_uint64 iFrame;
43356  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43357  ma_uint32 iChannel;
43358  for (iChannel = 0; iChannel < channels; iChannel += 1) {
43359  dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
43360  }
43361  }
43362 }
43363 #else
43364 static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43365 {
43366  ma_uint8* dst_u8 = (ma_uint8*)dst;
43367  const ma_uint8** src_u8 = (const ma_uint8**)src;
43368 
43369  if (channels == 1) {
43370  ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
43371  } else if (channels == 2) {
43372  ma_uint64 iFrame;
43373  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43374  dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
43375  dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
43376  }
43377  } else {
43378  ma_uint64 iFrame;
43379  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43380  ma_uint32 iChannel;
43381  for (iChannel = 0; iChannel < channels; iChannel += 1) {
43382  dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
43383  }
43384  }
43385  }
43386 }
43387 #endif
43388 
43389 MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43390 {
43391 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43392  ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
43393 #else
43394  ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
43395 #endif
43396 }
43397 
43398 
43399 static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43400 {
43401  ma_uint8** dst_u8 = (ma_uint8**)dst;
43402  const ma_uint8* src_u8 = (const ma_uint8*)src;
43403 
43404  ma_uint64 iFrame;
43405  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43406  ma_uint32 iChannel;
43407  for (iChannel = 0; iChannel < channels; iChannel += 1) {
43408  dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
43409  }
43410  }
43411 }
43412 
43413 static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43414 {
43415  ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
43416 }
43417 
43418 MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43419 {
43420 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43421  ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
43422 #else
43423  ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
43424 #endif
43425 }
43426 
43427 
43428 /* s16 */
43429 static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43430 {
43431  ma_uint8* dst_u8 = (ma_uint8*)dst;
43432  const ma_int16* src_s16 = (const ma_int16*)src;
43433 
43434  if (ditherMode == ma_dither_mode_none) {
43435  ma_uint64 i;
43436  for (i = 0; i < count; i += 1) {
43437  ma_int16 x = src_s16[i];
43438  x = (ma_int16)(x >> 8);
43439  x = (ma_int16)(x + 128);
43440  dst_u8[i] = (ma_uint8)x;
43441  }
43442  } else {
43443  ma_uint64 i;
43444  for (i = 0; i < count; i += 1) {
43445  ma_int16 x = src_s16[i];
43446 
43447  /* Dither. Don't overflow. */
43448  ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
43449  if ((x + dither) <= 0x7FFF) {
43450  x = (ma_int16)(x + dither);
43451  } else {
43452  x = 0x7FFF;
43453  }
43454 
43455  x = (ma_int16)(x >> 8);
43456  x = (ma_int16)(x + 128);
43457  dst_u8[i] = (ma_uint8)x;
43458  }
43459  }
43460 }
43461 
43462 static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43463 {
43464  ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
43465 }
43466 
43467 #if defined(MA_SUPPORT_SSE2)
43468 static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43469 {
43470  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
43471 }
43472 #endif
43473 #if defined(MA_SUPPORT_NEON)
43474 static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43475 {
43476  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
43477 }
43478 #endif
43479 
43480 MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43481 {
43482 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43483  ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
43484 #else
43485  # if defined(MA_SUPPORT_SSE2)
43486  if (ma_has_sse2()) {
43487  ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
43488  } else
43489  #elif defined(MA_SUPPORT_NEON)
43490  if (ma_has_neon()) {
43491  ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
43492  } else
43493  #endif
43494  {
43495  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
43496  }
43497 #endif
43498 }
43499 
43500 
43501 MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43502 {
43503  (void)ditherMode;
43504  ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
43505 }
43506 
43507 
43508 static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43509 {
43510  ma_uint8* dst_s24 = (ma_uint8*)dst;
43511  const ma_int16* src_s16 = (const ma_int16*)src;
43512 
43513  ma_uint64 i;
43514  for (i = 0; i < count; i += 1) {
43515  dst_s24[i*3+0] = 0;
43516  dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
43517  dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
43518  }
43519 
43520  (void)ditherMode;
43521 }
43522 
43523 static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43524 {
43525  ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
43526 }
43527 
43528 #if defined(MA_SUPPORT_SSE2)
43529 static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43530 {
43531  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
43532 }
43533 #endif
43534 #if defined(MA_SUPPORT_NEON)
43535 static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43536 {
43537  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
43538 }
43539 #endif
43540 
43541 MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43542 {
43543 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43544  ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
43545 #else
43546  # if defined(MA_SUPPORT_SSE2)
43547  if (ma_has_sse2()) {
43548  ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
43549  } else
43550  #elif defined(MA_SUPPORT_NEON)
43551  if (ma_has_neon()) {
43552  ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
43553  } else
43554  #endif
43555  {
43556  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
43557  }
43558 #endif
43559 }
43560 
43561 
43562 static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43563 {
43564  ma_int32* dst_s32 = (ma_int32*)dst;
43565  const ma_int16* src_s16 = (const ma_int16*)src;
43566 
43567  ma_uint64 i;
43568  for (i = 0; i < count; i += 1) {
43569  dst_s32[i] = src_s16[i] << 16;
43570  }
43571 
43572  (void)ditherMode;
43573 }
43574 
43575 static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43576 {
43577  ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
43578 }
43579 
43580 #if defined(MA_SUPPORT_SSE2)
43581 static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43582 {
43583  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
43584 }
43585 #endif
43586 #if defined(MA_SUPPORT_NEON)
43587 static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43588 {
43589  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
43590 }
43591 #endif
43592 
43593 MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43594 {
43595 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43596  ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
43597 #else
43598  # if defined(MA_SUPPORT_SSE2)
43599  if (ma_has_sse2()) {
43600  ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
43601  } else
43602  #elif defined(MA_SUPPORT_NEON)
43603  if (ma_has_neon()) {
43604  ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
43605  } else
43606  #endif
43607  {
43608  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
43609  }
43610 #endif
43611 }
43612 
43613 
43614 static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43615 {
43616  float* dst_f32 = (float*)dst;
43617  const ma_int16* src_s16 = (const ma_int16*)src;
43618 
43619  ma_uint64 i;
43620  for (i = 0; i < count; i += 1) {
43621  float x = (float)src_s16[i];
43622 
43623 #if 0
43624  /* The accurate way. */
43625  x = x + 32768.0f; /* -32768..32767 to 0..65535 */
43626  x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */
43627  x = x - 1; /* 0..2 to -1..1 */
43628 #else
43629  /* The fast way. */
43630  x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
43631 #endif
43632 
43633  dst_f32[i] = x;
43634  }
43635 
43636  (void)ditherMode;
43637 }
43638 
43639 static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43640 {
43641  ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
43642 }
43643 
43644 #if defined(MA_SUPPORT_SSE2)
43645 static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43646 {
43647  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
43648 }
43649 #endif
43650 #if defined(MA_SUPPORT_NEON)
43651 static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43652 {
43653  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
43654 }
43655 #endif
43656 
43657 MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43658 {
43659 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43660  ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
43661 #else
43662  # if defined(MA_SUPPORT_SSE2)
43663  if (ma_has_sse2()) {
43664  ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
43665  } else
43666  #elif defined(MA_SUPPORT_NEON)
43667  if (ma_has_neon()) {
43668  ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
43669  } else
43670  #endif
43671  {
43672  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
43673  }
43674 #endif
43675 }
43676 
43677 
43678 static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43679 {
43680  ma_int16* dst_s16 = (ma_int16*)dst;
43681  const ma_int16** src_s16 = (const ma_int16**)src;
43682 
43683  ma_uint64 iFrame;
43684  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43685  ma_uint32 iChannel;
43686  for (iChannel = 0; iChannel < channels; iChannel += 1) {
43687  dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
43688  }
43689  }
43690 }
43691 
43692 static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43693 {
43694  ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
43695 }
43696 
43697 MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43698 {
43699 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43700  ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
43701 #else
43702  ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
43703 #endif
43704 }
43705 
43706 
43707 static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43708 {
43709  ma_int16** dst_s16 = (ma_int16**)dst;
43710  const ma_int16* src_s16 = (const ma_int16*)src;
43711 
43712  ma_uint64 iFrame;
43713  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43714  ma_uint32 iChannel;
43715  for (iChannel = 0; iChannel < channels; iChannel += 1) {
43716  dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
43717  }
43718  }
43719 }
43720 
43721 static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43722 {
43723  ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
43724 }
43725 
43726 MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43727 {
43728 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43729  ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
43730 #else
43731  ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
43732 #endif
43733 }
43734 
43735 
43736 /* s24 */
43737 static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43738 {
43739  ma_uint8* dst_u8 = (ma_uint8*)dst;
43740  const ma_uint8* src_s24 = (const ma_uint8*)src;
43741 
43742  if (ditherMode == ma_dither_mode_none) {
43743  ma_uint64 i;
43744  for (i = 0; i < count; i += 1) {
43745  dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128);
43746  }
43747  } else {
43748  ma_uint64 i;
43749  for (i = 0; i < count; i += 1) {
43750  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);
43751 
43752  /* Dither. Don't overflow. */
43753  ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
43754  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
43755  x = x + dither;
43756  } else {
43757  x = 0x7FFFFFFF;
43758  }
43759 
43760  x = x >> 24;
43761  x = x + 128;
43762  dst_u8[i] = (ma_uint8)x;
43763  }
43764  }
43765 }
43766 
43767 static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43768 {
43769  ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
43770 }
43771 
43772 #if defined(MA_SUPPORT_SSE2)
43773 static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43774 {
43775  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
43776 }
43777 #endif
43778 #if defined(MA_SUPPORT_NEON)
43779 static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43780 {
43781  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
43782 }
43783 #endif
43784 
43785 MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43786 {
43787 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43788  ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
43789 #else
43790  # if defined(MA_SUPPORT_SSE2)
43791  if (ma_has_sse2()) {
43792  ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
43793  } else
43794  #elif defined(MA_SUPPORT_NEON)
43795  if (ma_has_neon()) {
43796  ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
43797  } else
43798  #endif
43799  {
43800  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
43801  }
43802 #endif
43803 }
43804 
43805 
43806 static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43807 {
43808  ma_int16* dst_s16 = (ma_int16*)dst;
43809  const ma_uint8* src_s24 = (const ma_uint8*)src;
43810 
43811  if (ditherMode == ma_dither_mode_none) {
43812  ma_uint64 i;
43813  for (i = 0; i < count; i += 1) {
43814  ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
43815  ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8);
43816  dst_s16[i] = (ma_int16)(dst_lo | dst_hi);
43817  }
43818  } else {
43819  ma_uint64 i;
43820  for (i = 0; i < count; i += 1) {
43821  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);
43822 
43823  /* Dither. Don't overflow. */
43824  ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
43825  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
43826  x = x + dither;
43827  } else {
43828  x = 0x7FFFFFFF;
43829  }
43830 
43831  x = x >> 16;
43832  dst_s16[i] = (ma_int16)x;
43833  }
43834  }
43835 }
43836 
43837 static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43838 {
43839  ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
43840 }
43841 
43842 #if defined(MA_SUPPORT_SSE2)
43843 static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43844 {
43845  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
43846 }
43847 #endif
43848 #if defined(MA_SUPPORT_NEON)
43849 static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43850 {
43851  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
43852 }
43853 #endif
43854 
43855 MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43856 {
43857 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43858  ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
43859 #else
43860  # if defined(MA_SUPPORT_SSE2)
43861  if (ma_has_sse2()) {
43862  ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
43863  } else
43864  #elif defined(MA_SUPPORT_NEON)
43865  if (ma_has_neon()) {
43866  ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
43867  } else
43868  #endif
43869  {
43870  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
43871  }
43872 #endif
43873 }
43874 
43875 
43876 MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43877 {
43878  (void)ditherMode;
43879 
43880  ma_copy_memory_64(dst, src, count * 3);
43881 }
43882 
43883 
43884 static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43885 {
43886  ma_int32* dst_s32 = (ma_int32*)dst;
43887  const ma_uint8* src_s24 = (const ma_uint8*)src;
43888 
43889  ma_uint64 i;
43890  for (i = 0; i < count; i += 1) {
43891  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);
43892  }
43893 
43894  (void)ditherMode;
43895 }
43896 
43897 static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43898 {
43899  ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
43900 }
43901 
43902 #if defined(MA_SUPPORT_SSE2)
43903 static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43904 {
43905  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
43906 }
43907 #endif
43908 #if defined(MA_SUPPORT_NEON)
43909 static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43910 {
43911  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
43912 }
43913 #endif
43914 
43915 MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43916 {
43917 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43918  ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
43919 #else
43920  # if defined(MA_SUPPORT_SSE2)
43921  if (ma_has_sse2()) {
43922  ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
43923  } else
43924  #elif defined(MA_SUPPORT_NEON)
43925  if (ma_has_neon()) {
43926  ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
43927  } else
43928  #endif
43929  {
43930  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
43931  }
43932 #endif
43933 }
43934 
43935 
43936 static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43937 {
43938  float* dst_f32 = (float*)dst;
43939  const ma_uint8* src_s24 = (const ma_uint8*)src;
43940 
43941  ma_uint64 i;
43942  for (i = 0; i < count; i += 1) {
43943  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);
43944 
43945 #if 0
43946  /* The accurate way. */
43947  x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
43948  x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
43949  x = x - 1; /* 0..2 to -1..1 */
43950 #else
43951  /* The fast way. */
43952  x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
43953 #endif
43954 
43955  dst_f32[i] = x;
43956  }
43957 
43958  (void)ditherMode;
43959 }
43960 
43961 static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43962 {
43963  ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
43964 }
43965 
43966 #if defined(MA_SUPPORT_SSE2)
43967 static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43968 {
43969  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
43970 }
43971 #endif
43972 #if defined(MA_SUPPORT_NEON)
43973 static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43974 {
43975  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
43976 }
43977 #endif
43978 
43979 MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43980 {
43981 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43982  ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
43983 #else
43984  # if defined(MA_SUPPORT_SSE2)
43985  if (ma_has_sse2()) {
43986  ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
43987  } else
43988  #elif defined(MA_SUPPORT_NEON)
43989  if (ma_has_neon()) {
43990  ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
43991  } else
43992  #endif
43993  {
43994  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
43995  }
43996 #endif
43997 }
43998 
43999 
44000 static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44001 {
44002  ma_uint8* dst8 = (ma_uint8*)dst;
44003  const ma_uint8** src8 = (const ma_uint8**)src;
44004 
44005  ma_uint64 iFrame;
44006  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44007  ma_uint32 iChannel;
44008  for (iChannel = 0; iChannel < channels; iChannel += 1) {
44009  dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
44010  dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
44011  dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
44012  }
44013  }
44014 }
44015 
44016 static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44017 {
44018  ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
44019 }
44020 
44021 MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44022 {
44023 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44024  ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
44025 #else
44026  ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
44027 #endif
44028 }
44029 
44030 
44031 static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44032 {
44033  ma_uint8** dst8 = (ma_uint8**)dst;
44034  const ma_uint8* src8 = (const ma_uint8*)src;
44035 
44036  ma_uint32 iFrame;
44037  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44038  ma_uint32 iChannel;
44039  for (iChannel = 0; iChannel < channels; iChannel += 1) {
44040  dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
44041  dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
44042  dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
44043  }
44044  }
44045 }
44046 
44047 static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44048 {
44049  ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
44050 }
44051 
44052 MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44053 {
44054 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44055  ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
44056 #else
44057  ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
44058 #endif
44059 }
44060 
44061 
44062 
44063 /* s32 */
44064 static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44065 {
44066  ma_uint8* dst_u8 = (ma_uint8*)dst;
44067  const ma_int32* src_s32 = (const ma_int32*)src;
44068 
44069  if (ditherMode == ma_dither_mode_none) {
44070  ma_uint64 i;
44071  for (i = 0; i < count; i += 1) {
44072  ma_int32 x = src_s32[i];
44073  x = x >> 24;
44074  x = x + 128;
44075  dst_u8[i] = (ma_uint8)x;
44076  }
44077  } else {
44078  ma_uint64 i;
44079  for (i = 0; i < count; i += 1) {
44080  ma_int32 x = src_s32[i];
44081 
44082  /* Dither. Don't overflow. */
44083  ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
44084  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
44085  x = x + dither;
44086  } else {
44087  x = 0x7FFFFFFF;
44088  }
44089 
44090  x = x >> 24;
44091  x = x + 128;
44092  dst_u8[i] = (ma_uint8)x;
44093  }
44094  }
44095 }
44096 
44097 static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44098 {
44099  ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
44100 }
44101 
44102 #if defined(MA_SUPPORT_SSE2)
44103 static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44104 {
44105  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
44106 }
44107 #endif
44108 #if defined(MA_SUPPORT_NEON)
44109 static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44110 {
44111  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
44112 }
44113 #endif
44114 
44115 MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44116 {
44117 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44118  ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
44119 #else
44120  # if defined(MA_SUPPORT_SSE2)
44121  if (ma_has_sse2()) {
44122  ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
44123  } else
44124  #elif defined(MA_SUPPORT_NEON)
44125  if (ma_has_neon()) {
44126  ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
44127  } else
44128  #endif
44129  {
44130  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
44131  }
44132 #endif
44133 }
44134 
44135 
44136 static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44137 {
44138  ma_int16* dst_s16 = (ma_int16*)dst;
44139  const ma_int32* src_s32 = (const ma_int32*)src;
44140 
44141  if (ditherMode == ma_dither_mode_none) {
44142  ma_uint64 i;
44143  for (i = 0; i < count; i += 1) {
44144  ma_int32 x = src_s32[i];
44145  x = x >> 16;
44146  dst_s16[i] = (ma_int16)x;
44147  }
44148  } else {
44149  ma_uint64 i;
44150  for (i = 0; i < count; i += 1) {
44151  ma_int32 x = src_s32[i];
44152 
44153  /* Dither. Don't overflow. */
44154  ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
44155  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
44156  x = x + dither;
44157  } else {
44158  x = 0x7FFFFFFF;
44159  }
44160 
44161  x = x >> 16;
44162  dst_s16[i] = (ma_int16)x;
44163  }
44164  }
44165 }
44166 
44167 static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44168 {
44169  ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
44170 }
44171 
44172 #if defined(MA_SUPPORT_SSE2)
44173 static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44174 {
44175  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
44176 }
44177 #endif
44178 #if defined(MA_SUPPORT_NEON)
44179 static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44180 {
44181  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
44182 }
44183 #endif
44184 
44185 MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44186 {
44187 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44188  ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
44189 #else
44190  # if defined(MA_SUPPORT_SSE2)
44191  if (ma_has_sse2()) {
44192  ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
44193  } else
44194  #elif defined(MA_SUPPORT_NEON)
44195  if (ma_has_neon()) {
44196  ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
44197  } else
44198  #endif
44199  {
44200  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
44201  }
44202 #endif
44203 }
44204 
44205 
44206 static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44207 {
44208  ma_uint8* dst_s24 = (ma_uint8*)dst;
44209  const ma_int32* src_s32 = (const ma_int32*)src;
44210 
44211  ma_uint64 i;
44212  for (i = 0; i < count; i += 1) {
44213  ma_uint32 x = (ma_uint32)src_s32[i];
44214  dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
44215  dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
44216  dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
44217  }
44218 
44219  (void)ditherMode; /* No dithering for s32 -> s24. */
44220 }
44221 
44222 static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44223 {
44224  ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
44225 }
44226 
44227 #if defined(MA_SUPPORT_SSE2)
44228 static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44229 {
44230  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
44231 }
44232 #endif
44233 #if defined(MA_SUPPORT_NEON)
44234 static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44235 {
44236  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
44237 }
44238 #endif
44239 
44240 MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44241 {
44242 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44243  ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
44244 #else
44245  # if defined(MA_SUPPORT_SSE2)
44246  if (ma_has_sse2()) {
44247  ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
44248  } else
44249  #elif defined(MA_SUPPORT_NEON)
44250  if (ma_has_neon()) {
44251  ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
44252  } else
44253  #endif
44254  {
44255  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
44256  }
44257 #endif
44258 }
44259 
44260 
44261 MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44262 {
44263  (void)ditherMode;
44264 
44265  ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
44266 }
44267 
44268 
44269 static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44270 {
44271  float* dst_f32 = (float*)dst;
44272  const ma_int32* src_s32 = (const ma_int32*)src;
44273 
44274  ma_uint64 i;
44275  for (i = 0; i < count; i += 1) {
44276  double x = src_s32[i];
44277 
44278 #if 0
44279  x = x + 2147483648.0;
44280  x = x * 0.0000000004656612873077392578125;
44281  x = x - 1;
44282 #else
44283  x = x / 2147483648.0;
44284 #endif
44285 
44286  dst_f32[i] = (float)x;
44287  }
44288 
44289  (void)ditherMode; /* No dithering for s32 -> f32. */
44290 }
44291 
44292 static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44293 {
44294  ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
44295 }
44296 
44297 #if defined(MA_SUPPORT_SSE2)
44298 static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44299 {
44300  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
44301 }
44302 #endif
44303 #if defined(MA_SUPPORT_NEON)
44304 static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44305 {
44306  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
44307 }
44308 #endif
44309 
44310 MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44311 {
44312 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44313  ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
44314 #else
44315  # if defined(MA_SUPPORT_SSE2)
44316  if (ma_has_sse2()) {
44317  ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
44318  } else
44319  #elif defined(MA_SUPPORT_NEON)
44320  if (ma_has_neon()) {
44321  ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
44322  } else
44323  #endif
44324  {
44325  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
44326  }
44327 #endif
44328 }
44329 
44330 
44331 static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44332 {
44333  ma_int32* dst_s32 = (ma_int32*)dst;
44334  const ma_int32** src_s32 = (const ma_int32**)src;
44335 
44336  ma_uint64 iFrame;
44337  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44338  ma_uint32 iChannel;
44339  for (iChannel = 0; iChannel < channels; iChannel += 1) {
44340  dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
44341  }
44342  }
44343 }
44344 
44345 static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44346 {
44347  ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
44348 }
44349 
44350 MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44351 {
44352 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44353  ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
44354 #else
44355  ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
44356 #endif
44357 }
44358 
44359 
44360 static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44361 {
44362  ma_int32** dst_s32 = (ma_int32**)dst;
44363  const ma_int32* src_s32 = (const ma_int32*)src;
44364 
44365  ma_uint64 iFrame;
44366  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44367  ma_uint32 iChannel;
44368  for (iChannel = 0; iChannel < channels; iChannel += 1) {
44369  dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
44370  }
44371  }
44372 }
44373 
44374 static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44375 {
44376  ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
44377 }
44378 
44379 MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44380 {
44381 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44382  ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
44383 #else
44384  ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
44385 #endif
44386 }
44387 
44388 
44389 /* f32 */
44390 static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44391 {
44392  ma_uint64 i;
44393 
44394  ma_uint8* dst_u8 = (ma_uint8*)dst;
44395  const float* src_f32 = (const float*)src;
44396 
44397  float ditherMin = 0;
44398  float ditherMax = 0;
44399  if (ditherMode != ma_dither_mode_none) {
44400  ditherMin = 1.0f / -128;
44401  ditherMax = 1.0f / 127;
44402  }
44403 
44404  for (i = 0; i < count; i += 1) {
44405  float x = src_f32[i];
44406  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44407  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44408  x = x + 1; /* -1..1 to 0..2 */
44409  x = x * 127.5f; /* 0..2 to 0..255 */
44410 
44411  dst_u8[i] = (ma_uint8)x;
44412  }
44413 }
44414 
44415 static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44416 {
44417  ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
44418 }
44419 
44420 #if defined(MA_SUPPORT_SSE2)
44421 static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44422 {
44423  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
44424 }
44425 #endif
44426 #if defined(MA_SUPPORT_NEON)
44427 static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44428 {
44429  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
44430 }
44431 #endif
44432 
44433 MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44434 {
44435 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44436  ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
44437 #else
44438  # if defined(MA_SUPPORT_SSE2)
44439  if (ma_has_sse2()) {
44440  ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
44441  } else
44442  #elif defined(MA_SUPPORT_NEON)
44443  if (ma_has_neon()) {
44444  ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
44445  } else
44446  #endif
44447  {
44448  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
44449  }
44450 #endif
44451 }
44452 
44453 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44454 static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44455 {
44456  ma_uint64 i;
44457 
44458  ma_int16* dst_s16 = (ma_int16*)dst;
44459  const float* src_f32 = (const float*)src;
44460 
44461  float ditherMin = 0;
44462  float ditherMax = 0;
44463  if (ditherMode != ma_dither_mode_none) {
44464  ditherMin = 1.0f / -32768;
44465  ditherMax = 1.0f / 32767;
44466  }
44467 
44468  for (i = 0; i < count; i += 1) {
44469  float x = src_f32[i];
44470  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44471  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44472 
44473 #if 0
44474  /* The accurate way. */
44475  x = x + 1; /* -1..1 to 0..2 */
44476  x = x * 32767.5f; /* 0..2 to 0..65535 */
44477  x = x - 32768.0f; /* 0...65535 to -32768..32767 */
44478 #else
44479  /* The fast way. */
44480  x = x * 32767.0f; /* -1..1 to -32767..32767 */
44481 #endif
44482 
44483  dst_s16[i] = (ma_int16)x;
44484  }
44485 }
44486 #else
44487 static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44488 {
44489  ma_uint64 i;
44490  ma_uint64 i4;
44491  ma_uint64 count4;
44492 
44493  ma_int16* dst_s16 = (ma_int16*)dst;
44494  const float* src_f32 = (const float*)src;
44495 
44496  float ditherMin = 0;
44497  float ditherMax = 0;
44498  if (ditherMode != ma_dither_mode_none) {
44499  ditherMin = 1.0f / -32768;
44500  ditherMax = 1.0f / 32767;
44501  }
44502 
44503  /* Unrolled. */
44504  i = 0;
44505  count4 = count >> 2;
44506  for (i4 = 0; i4 < count4; i4 += 1) {
44507  float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
44508  float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
44509  float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
44510  float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
44511 
44512  float x0 = src_f32[i+0];
44513  float x1 = src_f32[i+1];
44514  float x2 = src_f32[i+2];
44515  float x3 = src_f32[i+3];
44516 
44517  x0 = x0 + d0;
44518  x1 = x1 + d1;
44519  x2 = x2 + d2;
44520  x3 = x3 + d3;
44521 
44522  x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
44523  x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
44524  x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
44525  x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
44526 
44527  x0 = x0 * 32767.0f;
44528  x1 = x1 * 32767.0f;
44529  x2 = x2 * 32767.0f;
44530  x3 = x3 * 32767.0f;
44531 
44532  dst_s16[i+0] = (ma_int16)x0;
44533  dst_s16[i+1] = (ma_int16)x1;
44534  dst_s16[i+2] = (ma_int16)x2;
44535  dst_s16[i+3] = (ma_int16)x3;
44536 
44537  i += 4;
44538  }
44539 
44540  /* Leftover. */
44541  for (; i < count; i += 1) {
44542  float x = src_f32[i];
44543  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44544  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44545  x = x * 32767.0f; /* -1..1 to -32767..32767 */
44546 
44547  dst_s16[i] = (ma_int16)x;
44548  }
44549 }
44550 
44551 #if defined(MA_SUPPORT_SSE2)
44552 static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44553 {
44554  ma_uint64 i;
44555  ma_uint64 i8;
44556  ma_uint64 count8;
44557  ma_int16* dst_s16;
44558  const float* src_f32;
44559  float ditherMin;
44560  float ditherMax;
44561 
44562  /* Both the input and output buffers need to be aligned to 16 bytes. */
44563  if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
44564  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
44565  return;
44566  }
44567 
44568  dst_s16 = (ma_int16*)dst;
44569  src_f32 = (const float*)src;
44570 
44571  ditherMin = 0;
44572  ditherMax = 0;
44573  if (ditherMode != ma_dither_mode_none) {
44574  ditherMin = 1.0f / -32768;
44575  ditherMax = 1.0f / 32767;
44576  }
44577 
44578  i = 0;
44579 
44580  /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
44581  count8 = count >> 3;
44582  for (i8 = 0; i8 < count8; i8 += 1) {
44583  __m128 d0;
44584  __m128 d1;
44585  __m128 x0;
44586  __m128 x1;
44587 
44588  if (ditherMode == ma_dither_mode_none) {
44589  d0 = _mm_set1_ps(0);
44590  d1 = _mm_set1_ps(0);
44591  } else if (ditherMode == ma_dither_mode_rectangle) {
44592  d0 = _mm_set_ps(
44593  ma_dither_f32_rectangle(ditherMin, ditherMax),
44594  ma_dither_f32_rectangle(ditherMin, ditherMax),
44595  ma_dither_f32_rectangle(ditherMin, ditherMax),
44596  ma_dither_f32_rectangle(ditherMin, ditherMax)
44597  );
44598  d1 = _mm_set_ps(
44599  ma_dither_f32_rectangle(ditherMin, ditherMax),
44600  ma_dither_f32_rectangle(ditherMin, ditherMax),
44601  ma_dither_f32_rectangle(ditherMin, ditherMax),
44602  ma_dither_f32_rectangle(ditherMin, ditherMax)
44603  );
44604  } else {
44605  d0 = _mm_set_ps(
44606  ma_dither_f32_triangle(ditherMin, ditherMax),
44607  ma_dither_f32_triangle(ditherMin, ditherMax),
44608  ma_dither_f32_triangle(ditherMin, ditherMax),
44609  ma_dither_f32_triangle(ditherMin, ditherMax)
44610  );
44611  d1 = _mm_set_ps(
44612  ma_dither_f32_triangle(ditherMin, ditherMax),
44613  ma_dither_f32_triangle(ditherMin, ditherMax),
44614  ma_dither_f32_triangle(ditherMin, ditherMax),
44615  ma_dither_f32_triangle(ditherMin, ditherMax)
44616  );
44617  }
44618 
44619  x0 = *((__m128*)(src_f32 + i) + 0);
44620  x1 = *((__m128*)(src_f32 + i) + 1);
44621 
44622  x0 = _mm_add_ps(x0, d0);
44623  x1 = _mm_add_ps(x1, d1);
44624 
44625  x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
44626  x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
44627 
44628  _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
44629 
44630  i += 8;
44631  }
44632 
44633 
44634  /* Leftover. */
44635  for (; i < count; i += 1) {
44636  float x = src_f32[i];
44637  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44638  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44639  x = x * 32767.0f; /* -1..1 to -32767..32767 */
44640 
44641  dst_s16[i] = (ma_int16)x;
44642  }
44643 }
44644 #endif /* SSE2 */
44645 
44646 #if defined(MA_SUPPORT_NEON)
44647 static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44648 {
44649  ma_uint64 i;
44650  ma_uint64 i8;
44651  ma_uint64 count8;
44652  ma_int16* dst_s16;
44653  const float* src_f32;
44654  float ditherMin;
44655  float ditherMax;
44656 
44657  if (!ma_has_neon()) {
44658  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
44659  return;
44660  }
44661 
44662  /* Both the input and output buffers need to be aligned to 16 bytes. */
44663  if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
44664  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
44665  return;
44666  }
44667 
44668  dst_s16 = (ma_int16*)dst;
44669  src_f32 = (const float*)src;
44670 
44671  ditherMin = 0;
44672  ditherMax = 0;
44673  if (ditherMode != ma_dither_mode_none) {
44674  ditherMin = 1.0f / -32768;
44675  ditherMax = 1.0f / 32767;
44676  }
44677 
44678  i = 0;
44679 
44680  /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
44681  count8 = count >> 3;
44682  for (i8 = 0; i8 < count8; i8 += 1) {
44683  float32x4_t d0;
44684  float32x4_t d1;
44685  float32x4_t x0;
44686  float32x4_t x1;
44687  int32x4_t i0;
44688  int32x4_t i1;
44689 
44690  if (ditherMode == ma_dither_mode_none) {
44691  d0 = vmovq_n_f32(0);
44692  d1 = vmovq_n_f32(0);
44693  } else if (ditherMode == ma_dither_mode_rectangle) {
44694  float d0v[4];
44695  float d1v[4];
44696 
44697  d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44698  d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44699  d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44700  d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44701  d0 = vld1q_f32(d0v);
44702 
44703  d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44704  d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44705  d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44706  d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44707  d1 = vld1q_f32(d1v);
44708  } else {
44709  float d0v[4];
44710  float d1v[4];
44711 
44712  d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
44713  d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
44714  d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
44715  d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
44716  d0 = vld1q_f32(d0v);
44717 
44718  d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
44719  d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
44720  d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
44721  d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
44722  d1 = vld1q_f32(d1v);
44723  }
44724 
44725  x0 = *((float32x4_t*)(src_f32 + i) + 0);
44726  x1 = *((float32x4_t*)(src_f32 + i) + 1);
44727 
44728  x0 = vaddq_f32(x0, d0);
44729  x1 = vaddq_f32(x1, d1);
44730 
44731  x0 = vmulq_n_f32(x0, 32767.0f);
44732  x1 = vmulq_n_f32(x1, 32767.0f);
44733 
44734  i0 = vcvtq_s32_f32(x0);
44735  i1 = vcvtq_s32_f32(x1);
44736  *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
44737 
44738  i += 8;
44739  }
44740 
44741 
44742  /* Leftover. */
44743  for (; i < count; i += 1) {
44744  float x = src_f32[i];
44745  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44746  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44747  x = x * 32767.0f; /* -1..1 to -32767..32767 */
44748 
44749  dst_s16[i] = (ma_int16)x;
44750  }
44751 }
44752 #endif /* Neon */
44753 #endif /* MA_USE_REFERENCE_CONVERSION_APIS */
44754 
44755 MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44756 {
44757 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44758  ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
44759 #else
44760  # if defined(MA_SUPPORT_SSE2)
44761  if (ma_has_sse2()) {
44762  ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
44763  } else
44764  #elif defined(MA_SUPPORT_NEON)
44765  if (ma_has_neon()) {
44766  ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
44767  } else
44768  #endif
44769  {
44770  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
44771  }
44772 #endif
44773 }
44774 
44775 
44776 static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44777 {
44778  ma_uint8* dst_s24 = (ma_uint8*)dst;
44779  const float* src_f32 = (const float*)src;
44780 
44781  ma_uint64 i;
44782  for (i = 0; i < count; i += 1) {
44783  ma_int32 r;
44784  float x = src_f32[i];
44785  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44786 
44787 #if 0
44788  /* The accurate way. */
44789  x = x + 1; /* -1..1 to 0..2 */
44790  x = x * 8388607.5f; /* 0..2 to 0..16777215 */
44791  x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
44792 #else
44793  /* The fast way. */
44794  x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
44795 #endif
44796 
44797  r = (ma_int32)x;
44798  dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
44799  dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
44800  dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
44801  }
44802 
44803  (void)ditherMode; /* No dithering for f32 -> s24. */
44804 }
44805 
44806 static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44807 {
44808  ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
44809 }
44810 
44811 #if defined(MA_SUPPORT_SSE2)
44812 static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44813 {
44814  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
44815 }
44816 #endif
44817 #if defined(MA_SUPPORT_NEON)
44818 static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44819 {
44820  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
44821 }
44822 #endif
44823 
44824 MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44825 {
44826 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44827  ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
44828 #else
44829  # if defined(MA_SUPPORT_SSE2)
44830  if (ma_has_sse2()) {
44831  ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
44832  } else
44833  #elif defined(MA_SUPPORT_NEON)
44834  if (ma_has_neon()) {
44835  ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
44836  } else
44837  #endif
44838  {
44839  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
44840  }
44841 #endif
44842 }
44843 
44844 
44845 static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44846 {
44847  ma_int32* dst_s32 = (ma_int32*)dst;
44848  const float* src_f32 = (const float*)src;
44849 
44850  ma_uint32 i;
44851  for (i = 0; i < count; i += 1) {
44852  double x = src_f32[i];
44853  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44854 
44855 #if 0
44856  /* The accurate way. */
44857  x = x + 1; /* -1..1 to 0..2 */
44858  x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
44859  x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
44860 #else
44861  /* The fast way. */
44862  x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
44863 #endif
44864 
44865  dst_s32[i] = (ma_int32)x;
44866  }
44867 
44868  (void)ditherMode; /* No dithering for f32 -> s32. */
44869 }
44870 
44871 static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44872 {
44873  ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
44874 }
44875 
44876 #if defined(MA_SUPPORT_SSE2)
44877 static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44878 {
44879  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
44880 }
44881 #endif
44882 #if defined(MA_SUPPORT_NEON)
44883 static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44884 {
44885  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
44886 }
44887 #endif
44888 
44889 MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44890 {
44891 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44892  ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
44893 #else
44894  # if defined(MA_SUPPORT_SSE2)
44895  if (ma_has_sse2()) {
44896  ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
44897  } else
44898  #elif defined(MA_SUPPORT_NEON)
44899  if (ma_has_neon()) {
44900  ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
44901  } else
44902  #endif
44903  {
44904  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
44905  }
44906 #endif
44907 }
44908 
44909 
44910 MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44911 {
44912  (void)ditherMode;
44913 
44914  ma_copy_memory_64(dst, src, count * sizeof(float));
44915 }
44916 
44917 
44918 static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44919 {
44920  float* dst_f32 = (float*)dst;
44921  const float** src_f32 = (const float**)src;
44922 
44923  ma_uint64 iFrame;
44924  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44925  ma_uint32 iChannel;
44926  for (iChannel = 0; iChannel < channels; iChannel += 1) {
44927  dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
44928  }
44929  }
44930 }
44931 
44932 static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44933 {
44934  ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
44935 }
44936 
44937 MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44938 {
44939 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44940  ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
44941 #else
44942  ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
44943 #endif
44944 }
44945 
44946 
44947 static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44948 {
44949  float** dst_f32 = (float**)dst;
44950  const float* src_f32 = (const float*)src;
44951 
44952  ma_uint64 iFrame;
44953  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44954  ma_uint32 iChannel;
44955  for (iChannel = 0; iChannel < channels; iChannel += 1) {
44956  dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
44957  }
44958  }
44959 }
44960 
44961 static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44962 {
44963  ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
44964 }
44965 
44966 MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44967 {
44968 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44969  ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
44970 #else
44971  ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
44972 #endif
44973 }
44974 
44975 
44976 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
44977 {
44978  if (formatOut == formatIn) {
44979  ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
44980  return;
44981  }
44982 
44983  switch (formatIn)
44984  {
44985  case ma_format_u8:
44986  {
44987  switch (formatOut)
44988  {
44989  case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
44990  case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
44991  case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
44992  case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
44993  default: break;
44994  }
44995  } break;
44996 
44997  case ma_format_s16:
44998  {
44999  switch (formatOut)
45000  {
45001  case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
45002  case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
45003  case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
45004  case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
45005  default: break;
45006  }
45007  } break;
45008 
45009  case ma_format_s24:
45010  {
45011  switch (formatOut)
45012  {
45013  case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
45014  case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
45015  case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
45016  case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
45017  default: break;
45018  }
45019  } break;
45020 
45021  case ma_format_s32:
45022  {
45023  switch (formatOut)
45024  {
45025  case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
45026  case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
45027  case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
45028  case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
45029  default: break;
45030  }
45031  } break;
45032 
45033  case ma_format_f32:
45034  {
45035  switch (formatOut)
45036  {
45037  case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
45038  case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
45039  case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
45040  case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
45041  default: break;
45042  }
45043  } break;
45044 
45045  default: break;
45046  }
45047 }
45048 
45049 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)
45050 {
45051  ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
45052 }
45053 
45054 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
45055 {
45056  if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
45057  return; /* Invalid args. */
45058  }
45059 
45060  /* For efficiency we do this per format. */
45061  switch (format) {
45062  case ma_format_s16:
45063  {
45064  const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
45065  ma_uint64 iPCMFrame;
45066  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45067  ma_uint32 iChannel;
45068  for (iChannel = 0; iChannel < channels; ++iChannel) {
45069  ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
45070  pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
45071  }
45072  }
45073  } break;
45074 
45075  case ma_format_f32:
45076  {
45077  const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
45078  ma_uint64 iPCMFrame;
45079  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45080  ma_uint32 iChannel;
45081  for (iChannel = 0; iChannel < channels; ++iChannel) {
45082  float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
45083  pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
45084  }
45085  }
45086  } break;
45087 
45088  default:
45089  {
45090  ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
45091  ma_uint64 iPCMFrame;
45092  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45093  ma_uint32 iChannel;
45094  for (iChannel = 0; iChannel < channels; ++iChannel) {
45095  void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
45096  const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
45097  memcpy(pDst, pSrc, sampleSizeInBytes);
45098  }
45099  }
45100  } break;
45101  }
45102 }
45103 
45104 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
45105 {
45106  switch (format)
45107  {
45108  case ma_format_s16:
45109  {
45110  ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
45111  ma_uint64 iPCMFrame;
45112  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45113  ma_uint32 iChannel;
45114  for (iChannel = 0; iChannel < channels; ++iChannel) {
45115  const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
45116  pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
45117  }
45118  }
45119  } break;
45120 
45121  case ma_format_f32:
45122  {
45123  float* pDstF32 = (float*)pInterleavedPCMFrames;
45124  ma_uint64 iPCMFrame;
45125  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45126  ma_uint32 iChannel;
45127  for (iChannel = 0; iChannel < channels; ++iChannel) {
45128  const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
45129  pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
45130  }
45131  }
45132  } break;
45133 
45134  default:
45135  {
45136  ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
45137  ma_uint64 iPCMFrame;
45138  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45139  ma_uint32 iChannel;
45140  for (iChannel = 0; iChannel < channels; ++iChannel) {
45141  void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
45142  const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
45143  memcpy(pDst, pSrc, sampleSizeInBytes);
45144  }
45145  }
45146  } break;
45147  }
45148 }
45149 
45150 
45151 /**************************************************************************************************************************************************************
45152 
45153 Biquad Filter
45154 
45155 **************************************************************************************************************************************************************/
45156 #ifndef MA_BIQUAD_FIXED_POINT_SHIFT
45157 #define MA_BIQUAD_FIXED_POINT_SHIFT 14
45158 #endif
45159 
45160 static ma_int32 ma_biquad_float_to_fp(double x)
45161 {
45162  return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
45163 }
45164 
45165 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)
45166 {
45167  ma_biquad_config config;
45168 
45169  MA_ZERO_OBJECT(&config);
45170  config.format = format;
45171  config.channels = channels;
45172  config.b0 = b0;
45173  config.b1 = b1;
45174  config.b2 = b2;
45175  config.a0 = a0;
45176  config.a1 = a1;
45177  config.a2 = a2;
45178 
45179  return config;
45180 }
45181 
45182 
45183 typedef struct
45184 {
45185  size_t sizeInBytes;
45186  size_t r1Offset;
45187  size_t r2Offset;
45188 } ma_biquad_heap_layout;
45189 
45190 static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout)
45191 {
45192  MA_ASSERT(pHeapLayout != NULL);
45193 
45194  MA_ZERO_OBJECT(pHeapLayout);
45195 
45196  if (pConfig == NULL) {
45197  return MA_INVALID_ARGS;
45198  }
45199 
45200  if (pConfig->channels == 0) {
45201  return MA_INVALID_ARGS;
45202  }
45203 
45204  pHeapLayout->sizeInBytes = 0;
45205 
45206  /* R0 */
45207  pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
45208  pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
45209 
45210  /* R1 */
45211  pHeapLayout->r2Offset = pHeapLayout->sizeInBytes;
45212  pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
45213 
45214  /* Make sure allocation size is aligned. */
45215  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
45216 
45217  return MA_SUCCESS;
45218 }
45219 
45220 MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes)
45221 {
45222  ma_result result;
45223  ma_biquad_heap_layout heapLayout;
45224 
45225  if (pHeapSizeInBytes == NULL) {
45226  return MA_INVALID_ARGS;
45227  }
45228 
45229  *pHeapSizeInBytes = 0;
45230 
45231  result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
45232  if (result != MA_SUCCESS) {
45233  return result;
45234  }
45235 
45236  *pHeapSizeInBytes = heapLayout.sizeInBytes;
45237 
45238  return MA_SUCCESS;
45239 }
45240 
45241 MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ)
45242 {
45243  ma_result result;
45244  ma_biquad_heap_layout heapLayout;
45245 
45246  if (pBQ == NULL) {
45247  return MA_INVALID_ARGS;
45248  }
45249 
45250  MA_ZERO_OBJECT(pBQ);
45251 
45252  result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
45253  if (result != MA_SUCCESS) {
45254  return result;
45255  }
45256 
45257  pBQ->_pHeap = pHeap;
45258  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
45259 
45260  pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
45261  pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset);
45262 
45263  return ma_biquad_reinit(pConfig, pBQ);
45264 }
45265 
45266 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ)
45267 {
45268  ma_result result;
45269  size_t heapSizeInBytes;
45270  void* pHeap;
45271 
45272  result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes);
45273  if (result != MA_SUCCESS) {
45274  return result;
45275  }
45276 
45277  if (heapSizeInBytes > 0) {
45278  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45279  if (pHeap == NULL) {
45280  return MA_OUT_OF_MEMORY;
45281  }
45282  } else {
45283  pHeap = NULL;
45284  }
45285 
45286  result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ);
45287  if (result != MA_SUCCESS) {
45288  ma_free(pHeap, pAllocationCallbacks);
45289  return result;
45290  }
45291 
45292  pBQ->_ownsHeap = MA_TRUE;
45293  return MA_SUCCESS;
45294 }
45295 
45296 MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks)
45297 {
45298  if (pBQ == NULL) {
45299  return;
45300  }
45301 
45302  if (pBQ->_ownsHeap) {
45303  ma_free(pBQ->_pHeap, pAllocationCallbacks);
45304  }
45305 }
45306 
45307 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
45308 {
45309  if (pBQ == NULL || pConfig == NULL) {
45310  return MA_INVALID_ARGS;
45311  }
45312 
45313  if (pConfig->a0 == 0) {
45314  return MA_INVALID_ARGS; /* Division by zero. */
45315  }
45316 
45317  /* Only supporting f32 and s16. */
45318  if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
45319  return MA_INVALID_ARGS;
45320  }
45321 
45322  /* The format cannot be changed after initialization. */
45323  if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
45324  return MA_INVALID_OPERATION;
45325  }
45326 
45327  /* The channel count cannot be changed after initialization. */
45328  if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
45329  return MA_INVALID_OPERATION;
45330  }
45331 
45332 
45333  pBQ->format = pConfig->format;
45334  pBQ->channels = pConfig->channels;
45335 
45336  /* Normalize. */
45337  if (pConfig->format == ma_format_f32) {
45338  pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
45339  pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
45340  pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
45341  pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
45342  pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
45343  } else {
45344  pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
45345  pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
45346  pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
45347  pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
45348  pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
45349  }
45350 
45351  return MA_SUCCESS;
45352 }
45353 
45354 MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ)
45355 {
45356  if (pBQ == NULL) {
45357  return MA_INVALID_ARGS;
45358  }
45359 
45360  if (pBQ->format == ma_format_f32) {
45361  pBQ->pR1->f32 = 0;
45362  pBQ->pR2->f32 = 0;
45363  } else {
45364  pBQ->pR1->s32 = 0;
45365  pBQ->pR2->s32 = 0;
45366  }
45367 
45368  return MA_SUCCESS;
45369 }
45370 
45371 static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
45372 {
45373  ma_uint32 c;
45374  const ma_uint32 channels = pBQ->channels;
45375  const float b0 = pBQ->b0.f32;
45376  const float b1 = pBQ->b1.f32;
45377  const float b2 = pBQ->b2.f32;
45378  const float a1 = pBQ->a1.f32;
45379  const float a2 = pBQ->a2.f32;
45380 
45381  MA_ASSUME(channels > 0);
45382  for (c = 0; c < channels; c += 1) {
45383  float r1 = pBQ->pR1[c].f32;
45384  float r2 = pBQ->pR2[c].f32;
45385  float x = pX[c];
45386  float y;
45387 
45388  y = b0*x + r1;
45389  r1 = b1*x - a1*y + r2;
45390  r2 = b2*x - a2*y;
45391 
45392  pY[c] = y;
45393  pBQ->pR1[c].f32 = r1;
45394  pBQ->pR2[c].f32 = r2;
45395  }
45396 }
45397 
45398 static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
45399 {
45400  ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
45401 }
45402 
45403 static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
45404 {
45405  ma_uint32 c;
45406  const ma_uint32 channels = pBQ->channels;
45407  const ma_int32 b0 = pBQ->b0.s32;
45408  const ma_int32 b1 = pBQ->b1.s32;
45409  const ma_int32 b2 = pBQ->b2.s32;
45410  const ma_int32 a1 = pBQ->a1.s32;
45411  const ma_int32 a2 = pBQ->a2.s32;
45412 
45413  MA_ASSUME(channels > 0);
45414  for (c = 0; c < channels; c += 1) {
45415  ma_int32 r1 = pBQ->pR1[c].s32;
45416  ma_int32 r2 = pBQ->pR2[c].s32;
45417  ma_int32 x = pX[c];
45418  ma_int32 y;
45419 
45420  y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
45421  r1 = (b1*x - a1*y + r2);
45422  r2 = (b2*x - a2*y);
45423 
45424  pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
45425  pBQ->pR1[c].s32 = r1;
45426  pBQ->pR2[c].s32 = r2;
45427  }
45428 }
45429 
45430 static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
45431 {
45432  ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
45433 }
45434 
45435 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45436 {
45437  ma_uint32 n;
45438 
45439  if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
45440  return MA_INVALID_ARGS;
45441  }
45442 
45443  /* 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. */
45444 
45445  if (pBQ->format == ma_format_f32) {
45446  /* */ float* pY = ( float*)pFramesOut;
45447  const float* pX = (const float*)pFramesIn;
45448 
45449  for (n = 0; n < frameCount; n += 1) {
45450  ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
45451  pY += pBQ->channels;
45452  pX += pBQ->channels;
45453  }
45454  } else if (pBQ->format == ma_format_s16) {
45455  /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
45456  const ma_int16* pX = (const ma_int16*)pFramesIn;
45457 
45458  for (n = 0; n < frameCount; n += 1) {
45459  ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
45460  pY += pBQ->channels;
45461  pX += pBQ->channels;
45462  }
45463  } else {
45464  MA_ASSERT(MA_FALSE);
45465  return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
45466  }
45467 
45468  return MA_SUCCESS;
45469 }
45470 
45471 MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ)
45472 {
45473  if (pBQ == NULL) {
45474  return 0;
45475  }
45476 
45477  return 2;
45478 }
45479 
45480 
45481 /**************************************************************************************************************************************************************
45482 
45483 Low-Pass Filter
45484 
45485 **************************************************************************************************************************************************************/
45486 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
45487 {
45488  ma_lpf1_config config;
45489 
45490  MA_ZERO_OBJECT(&config);
45491  config.format = format;
45492  config.channels = channels;
45493  config.sampleRate = sampleRate;
45494  config.cutoffFrequency = cutoffFrequency;
45495  config.q = 0.5;
45496 
45497  return config;
45498 }
45499 
45500 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
45501 {
45502  ma_lpf2_config config;
45503 
45504  MA_ZERO_OBJECT(&config);
45505  config.format = format;
45506  config.channels = channels;
45507  config.sampleRate = sampleRate;
45508  config.cutoffFrequency = cutoffFrequency;
45509  config.q = q;
45510 
45511  /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
45512  if (config.q == 0) {
45513  config.q = 0.707107;
45514  }
45515 
45516  return config;
45517 }
45518 
45519 
45520 typedef struct
45521 {
45522  size_t sizeInBytes;
45523  size_t r1Offset;
45524 } ma_lpf1_heap_layout;
45525 
45526 static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout)
45527 {
45528  MA_ASSERT(pHeapLayout != NULL);
45529 
45530  MA_ZERO_OBJECT(pHeapLayout);
45531 
45532  if (pConfig == NULL) {
45533  return MA_INVALID_ARGS;
45534  }
45535 
45536  if (pConfig->channels == 0) {
45537  return MA_INVALID_ARGS;
45538  }
45539 
45540  pHeapLayout->sizeInBytes = 0;
45541 
45542  /* R1 */
45543  pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
45544  pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
45545 
45546  /* Make sure allocation size is aligned. */
45547  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
45548 
45549  return MA_SUCCESS;
45550 }
45551 
45552 MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes)
45553 {
45554  ma_result result;
45555  ma_lpf1_heap_layout heapLayout;
45556 
45557  if (pHeapSizeInBytes == NULL) {
45558  return MA_INVALID_ARGS;
45559  }
45560 
45561  result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
45562  if (result != MA_SUCCESS) {
45563  return result;
45564  }
45565 
45566  *pHeapSizeInBytes = heapLayout.sizeInBytes;
45567 
45568  return MA_SUCCESS;
45569 }
45570 
45571 MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF)
45572 {
45573  ma_result result;
45574  ma_lpf1_heap_layout heapLayout;
45575 
45576  if (pLPF == NULL) {
45577  return MA_INVALID_ARGS;
45578  }
45579 
45580  MA_ZERO_OBJECT(pLPF);
45581 
45582  result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
45583  if (result != MA_SUCCESS) {
45584  return result;
45585  }
45586 
45587  pLPF->_pHeap = pHeap;
45588  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
45589 
45590  pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
45591 
45592  return ma_lpf1_reinit(pConfig, pLPF);
45593 }
45594 
45595 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF)
45596 {
45597  ma_result result;
45598  size_t heapSizeInBytes;
45599  void* pHeap;
45600 
45601  result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes);
45602  if (result != MA_SUCCESS) {
45603  return result;
45604  }
45605 
45606  if (heapSizeInBytes > 0) {
45607  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45608  if (pHeap == NULL) {
45609  return MA_OUT_OF_MEMORY;
45610  }
45611  } else {
45612  pHeap = NULL;
45613  }
45614 
45615  result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF);
45616  if (result != MA_SUCCESS) {
45617  ma_free(pHeap, pAllocationCallbacks);
45618  return result;
45619  }
45620 
45621  pLPF->_ownsHeap = MA_TRUE;
45622  return MA_SUCCESS;
45623 }
45624 
45625 MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
45626 {
45627  if (pLPF == NULL) {
45628  return;
45629  }
45630 
45631  if (pLPF->_ownsHeap) {
45632  ma_free(pLPF->_pHeap, pAllocationCallbacks);
45633  }
45634 }
45635 
45636 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
45637 {
45638  double a;
45639 
45640  if (pLPF == NULL || pConfig == NULL) {
45641  return MA_INVALID_ARGS;
45642  }
45643 
45644  /* Only supporting f32 and s16. */
45645  if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
45646  return MA_INVALID_ARGS;
45647  }
45648 
45649  /* The format cannot be changed after initialization. */
45650  if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
45651  return MA_INVALID_OPERATION;
45652  }
45653 
45654  /* The channel count cannot be changed after initialization. */
45655  if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
45656  return MA_INVALID_OPERATION;
45657  }
45658 
45659  pLPF->format = pConfig->format;
45660  pLPF->channels = pConfig->channels;
45661 
45662  a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
45663  if (pConfig->format == ma_format_f32) {
45664  pLPF->a.f32 = (float)a;
45665  } else {
45666  pLPF->a.s32 = ma_biquad_float_to_fp(a);
45667  }
45668 
45669  return MA_SUCCESS;
45670 }
45671 
45672 MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF)
45673 {
45674  if (pLPF == NULL) {
45675  return MA_INVALID_ARGS;
45676  }
45677 
45678  if (pLPF->format == ma_format_f32) {
45679  pLPF->a.f32 = 0;
45680  } else {
45681  pLPF->a.s32 = 0;
45682  }
45683 
45684  return MA_SUCCESS;
45685 }
45686 
45687 static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
45688 {
45689  ma_uint32 c;
45690  const ma_uint32 channels = pLPF->channels;
45691  const float a = pLPF->a.f32;
45692  const float b = 1 - a;
45693 
45694  MA_ASSUME(channels > 0);
45695  for (c = 0; c < channels; c += 1) {
45696  float r1 = pLPF->pR1[c].f32;
45697  float x = pX[c];
45698  float y;
45699 
45700  y = b*x + a*r1;
45701 
45702  pY[c] = y;
45703  pLPF->pR1[c].f32 = y;
45704  }
45705 }
45706 
45707 static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)
45708 {
45709  ma_uint32 c;
45710  const ma_uint32 channels = pLPF->channels;
45711  const ma_int32 a = pLPF->a.s32;
45712  const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
45713 
45714  MA_ASSUME(channels > 0);
45715  for (c = 0; c < channels; c += 1) {
45716  ma_int32 r1 = pLPF->pR1[c].s32;
45717  ma_int32 x = pX[c];
45718  ma_int32 y;
45719 
45720  y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
45721 
45722  pY[c] = (ma_int16)y;
45723  pLPF->pR1[c].s32 = (ma_int32)y;
45724  }
45725 }
45726 
45727 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45728 {
45729  ma_uint32 n;
45730 
45731  if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
45732  return MA_INVALID_ARGS;
45733  }
45734 
45735  /* 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. */
45736 
45737  if (pLPF->format == ma_format_f32) {
45738  /* */ float* pY = ( float*)pFramesOut;
45739  const float* pX = (const float*)pFramesIn;
45740 
45741  for (n = 0; n < frameCount; n += 1) {
45742  ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);
45743  pY += pLPF->channels;
45744  pX += pLPF->channels;
45745  }
45746  } else if (pLPF->format == ma_format_s16) {
45747  /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
45748  const ma_int16* pX = (const ma_int16*)pFramesIn;
45749 
45750  for (n = 0; n < frameCount; n += 1) {
45751  ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);
45752  pY += pLPF->channels;
45753  pX += pLPF->channels;
45754  }
45755  } else {
45756  MA_ASSERT(MA_FALSE);
45757  return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
45758  }
45759 
45760  return MA_SUCCESS;
45761 }
45762 
45763 MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF)
45764 {
45765  if (pLPF == NULL) {
45766  return 0;
45767  }
45768 
45769  return 1;
45770 }
45771 
45772 
45773 static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)
45774 {
45775  ma_biquad_config bqConfig;
45776  double q;
45777  double w;
45778  double s;
45779  double c;
45780  double a;
45781 
45782  MA_ASSERT(pConfig != NULL);
45783 
45784  q = pConfig->q;
45785  w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
45786  s = ma_sind(w);
45787  c = ma_cosd(w);
45788  a = s / (2*q);
45789 
45790  bqConfig.b0 = (1 - c) / 2;
45791  bqConfig.b1 = 1 - c;
45792  bqConfig.b2 = (1 - c) / 2;
45793  bqConfig.a0 = 1 + a;
45794  bqConfig.a1 = -2 * c;
45795  bqConfig.a2 = 1 - a;
45796 
45797  bqConfig.format = pConfig->format;
45798  bqConfig.channels = pConfig->channels;
45799 
45800  return bqConfig;
45801 }
45802 
45803 MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes)
45804 {
45805  ma_biquad_config bqConfig;
45806  bqConfig = ma_lpf2__get_biquad_config(pConfig);
45807 
45808  return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
45809 }
45810 
45811 MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF)
45812 {
45813  ma_result result;
45814  ma_biquad_config bqConfig;
45815 
45816  if (pLPF == NULL) {
45817  return MA_INVALID_ARGS;
45818  }
45819 
45820  MA_ZERO_OBJECT(pLPF);
45821 
45822  if (pConfig == NULL) {
45823  return MA_INVALID_ARGS;
45824  }
45825 
45826  bqConfig = ma_lpf2__get_biquad_config(pConfig);
45827  result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq);
45828  if (result != MA_SUCCESS) {
45829  return result;
45830  }
45831 
45832  return MA_SUCCESS;
45833 }
45834 
45835 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF)
45836 {
45837  ma_result result;
45838  size_t heapSizeInBytes;
45839  void* pHeap;
45840 
45841  result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes);
45842  if (result != MA_SUCCESS) {
45843  return result;
45844  }
45845 
45846  if (heapSizeInBytes > 0) {
45847  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45848  if (pHeap == NULL) {
45849  return MA_OUT_OF_MEMORY;
45850  }
45851  } else {
45852  pHeap = NULL;
45853  }
45854 
45855  result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF);
45856  if (result != MA_SUCCESS) {
45857  ma_free(pHeap, pAllocationCallbacks);
45858  return result;
45859  }
45860 
45861  pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
45862  return MA_SUCCESS;
45863 }
45864 
45865 MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
45866 {
45867  if (pLPF == NULL) {
45868  return;
45869  }
45870 
45871  ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
45872 }
45873 
45874 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
45875 {
45876  ma_result result;
45877  ma_biquad_config bqConfig;
45878 
45879  if (pLPF == NULL || pConfig == NULL) {
45880  return MA_INVALID_ARGS;
45881  }
45882 
45883  bqConfig = ma_lpf2__get_biquad_config(pConfig);
45884  result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
45885  if (result != MA_SUCCESS) {
45886  return result;
45887  }
45888 
45889  return MA_SUCCESS;
45890 }
45891 
45892 MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF)
45893 {
45894  if (pLPF == NULL) {
45895  return MA_INVALID_ARGS;
45896  }
45897 
45898  ma_biquad_clear_cache(&pLPF->bq);
45899 
45900  return MA_SUCCESS;
45901 }
45902 
45903 static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
45904 {
45905  ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
45906 }
45907 
45908 static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)
45909 {
45910  ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
45911 }
45912 
45913 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45914 {
45915  if (pLPF == NULL) {
45916  return MA_INVALID_ARGS;
45917  }
45918 
45919  return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
45920 }
45921 
45922 MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF)
45923 {
45924  if (pLPF == NULL) {
45925  return 0;
45926  }
45927 
45928  return ma_biquad_get_latency(&pLPF->bq);
45929 }
45930 
45931 
45932 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
45933 {
45934  ma_lpf_config config;
45935 
45936  MA_ZERO_OBJECT(&config);
45937  config.format = format;
45938  config.channels = channels;
45939  config.sampleRate = sampleRate;
45940  config.cutoffFrequency = cutoffFrequency;
45941  config.order = ma_min(order, MA_MAX_FILTER_ORDER);
45942 
45943  return config;
45944 }
45945 
45946 
45947 typedef struct
45948 {
45949  size_t sizeInBytes;
45950  size_t lpf1Offset;
45951  size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */
45952 } ma_lpf_heap_layout;
45953 
45954 static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count)
45955 {
45956  MA_ASSERT(pLPF1Count != NULL);
45957  MA_ASSERT(pLPF2Count != NULL);
45958 
45959  *pLPF1Count = order % 2;
45960  *pLPF2Count = order / 2;
45961 }
45962 
45963 static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout)
45964 {
45965  ma_result result;
45966  ma_uint32 lpf1Count;
45967  ma_uint32 lpf2Count;
45968  ma_uint32 ilpf1;
45969  ma_uint32 ilpf2;
45970 
45971  MA_ASSERT(pHeapLayout != NULL);
45972 
45973  MA_ZERO_OBJECT(pHeapLayout);
45974 
45975  if (pConfig == NULL) {
45976  return MA_INVALID_ARGS;
45977  }
45978 
45979  if (pConfig->channels == 0) {
45980  return MA_INVALID_ARGS;
45981  }
45982 
45983  if (pConfig->order > MA_MAX_FILTER_ORDER) {
45984  return MA_INVALID_ARGS;
45985  }
45986 
45987  ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
45988 
45989  pHeapLayout->sizeInBytes = 0;
45990 
45991  /* LPF 1 */
45992  pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes;
45993  for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
45994  size_t lpf1HeapSizeInBytes;
45995  ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
45996 
45997  result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
45998  if (result != MA_SUCCESS) {
45999  return result;
46000  }
46001 
46002  pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes;
46003  }
46004 
46005  /* LPF 2*/
46006  pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes;
46007  for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
46008  size_t lpf2HeapSizeInBytes;
46009  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. */
46010 
46011  result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
46012  if (result != MA_SUCCESS) {
46013  return result;
46014  }
46015 
46016  pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes;
46017  }
46018 
46019  /* Make sure allocation size is aligned. */
46020  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
46021 
46022  return MA_SUCCESS;
46023 }
46024 
46025 static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew)
46026 {
46027  ma_result result;
46028  ma_uint32 lpf1Count;
46029  ma_uint32 lpf2Count;
46030  ma_uint32 ilpf1;
46031  ma_uint32 ilpf2;
46032  ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */
46033 
46034  if (pLPF == NULL || pConfig == NULL) {
46035  return MA_INVALID_ARGS;
46036  }
46037 
46038  /* Only supporting f32 and s16. */
46039  if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
46040  return MA_INVALID_ARGS;
46041  }
46042 
46043  /* The format cannot be changed after initialization. */
46044  if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
46045  return MA_INVALID_OPERATION;
46046  }
46047 
46048  /* The channel count cannot be changed after initialization. */
46049  if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
46050  return MA_INVALID_OPERATION;
46051  }
46052 
46053  if (pConfig->order > MA_MAX_FILTER_ORDER) {
46054  return MA_INVALID_ARGS;
46055  }
46056 
46057  ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
46058 
46059  /* The filter order can't change between reinits. */
46060  if (!isNew) {
46061  if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {
46062  return MA_INVALID_OPERATION;
46063  }
46064  }
46065 
46066  if (isNew) {
46067  result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
46068  if (result != MA_SUCCESS) {
46069  return result;
46070  }
46071 
46072  pLPF->_pHeap = pHeap;
46073  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
46074 
46075  pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset);
46076  pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset);
46077  } else {
46078  MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */
46079  }
46080 
46081  for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
46082  ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
46083 
46084  if (isNew) {
46085  size_t lpf1HeapSizeInBytes;
46086 
46087  result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
46088  if (result == MA_SUCCESS) {
46089  result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]);
46090  }
46091  } else {
46092  result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]);
46093  }
46094 
46095  if (result != MA_SUCCESS) {
46096  ma_uint32 jlpf1;
46097 
46098  for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) {
46099  ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46100  }
46101 
46102  return result;
46103  }
46104  }
46105 
46106  for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
46107  ma_lpf2_config lpf2Config;
46108  double q;
46109  double a;
46110 
46111  /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
46112  if (lpf1Count == 1) {
46113  a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
46114  } else {
46115  a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
46116  }
46117  q = 1 / (2*ma_cosd(a));
46118 
46119  lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
46120 
46121  if (isNew) {
46122  size_t lpf2HeapSizeInBytes;
46123 
46124  result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
46125  if (result == MA_SUCCESS) {
46126  result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]);
46127  }
46128  } else {
46129  result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]);
46130  }
46131 
46132  if (result != MA_SUCCESS) {
46133  ma_uint32 jlpf1;
46134  ma_uint32 jlpf2;
46135 
46136  for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) {
46137  ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46138  }
46139 
46140  for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) {
46141  ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46142  }
46143 
46144  return result;
46145  }
46146  }
46147 
46148  pLPF->lpf1Count = lpf1Count;
46149  pLPF->lpf2Count = lpf2Count;
46150  pLPF->format = pConfig->format;
46151  pLPF->channels = pConfig->channels;
46152  pLPF->sampleRate = pConfig->sampleRate;
46153 
46154  return MA_SUCCESS;
46155 }
46156 
46157 MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes)
46158 {
46159  ma_result result;
46160  ma_lpf_heap_layout heapLayout;
46161 
46162  if (pHeapSizeInBytes == NULL) {
46163  return MA_INVALID_ARGS;
46164  }
46165 
46166  *pHeapSizeInBytes = 0;
46167 
46168  result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
46169  if (result != MA_SUCCESS) {
46170  return result;
46171  }
46172 
46173  *pHeapSizeInBytes = heapLayout.sizeInBytes;
46174 
46175  return result;
46176 }
46177 
46178 MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF)
46179 {
46180  if (pLPF == NULL) {
46181  return MA_INVALID_ARGS;
46182  }
46183 
46184  MA_ZERO_OBJECT(pLPF);
46185 
46186  return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);
46187 }
46188 
46189 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF)
46190 {
46191  ma_result result;
46192  size_t heapSizeInBytes;
46193  void* pHeap;
46194 
46195  result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes);
46196  if (result != MA_SUCCESS) {
46197  return result;
46198  }
46199 
46200  if (heapSizeInBytes > 0) {
46201  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46202  if (pHeap == NULL) {
46203  return MA_OUT_OF_MEMORY;
46204  }
46205  } else {
46206  pHeap = NULL;
46207  }
46208 
46209  result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF);
46210  if (result != MA_SUCCESS) {
46211  ma_free(pHeap, pAllocationCallbacks);
46212  return result;
46213  }
46214 
46215  pLPF->_ownsHeap = MA_TRUE;
46216  return MA_SUCCESS;
46217 }
46218 
46219 MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
46220 {
46221  ma_uint32 ilpf1;
46222  ma_uint32 ilpf2;
46223 
46224  if (pLPF == NULL) {
46225  return;
46226  }
46227 
46228  for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46229  ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks);
46230  }
46231 
46232  for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46233  ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks);
46234  }
46235 
46236  if (pLPF->_ownsHeap) {
46237  ma_free(pLPF->_pHeap, pAllocationCallbacks);
46238  }
46239 }
46240 
46241 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
46242 {
46243  return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE);
46244 }
46245 
46246 MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF)
46247 {
46248  ma_uint32 ilpf1;
46249  ma_uint32 ilpf2;
46250 
46251  if (pLPF == NULL) {
46252  return MA_INVALID_ARGS;
46253  }
46254 
46255  for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46256  ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]);
46257  }
46258 
46259  for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46260  ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]);
46261  }
46262 
46263  return MA_SUCCESS;
46264 }
46265 
46266 static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
46267 {
46268  ma_uint32 ilpf1;
46269  ma_uint32 ilpf2;
46270 
46271  MA_ASSERT(pLPF->format == ma_format_f32);
46272 
46273  MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
46274 
46275  for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46276  ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY);
46277  }
46278 
46279  for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46280  ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY);
46281  }
46282 }
46283 
46284 static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)
46285 {
46286  ma_uint32 ilpf1;
46287  ma_uint32 ilpf2;
46288 
46289  MA_ASSERT(pLPF->format == ma_format_s16);
46290 
46291  MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
46292 
46293  for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46294  ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY);
46295  }
46296 
46297  for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46298  ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY);
46299  }
46300 }
46301 
46302 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46303 {
46304  ma_result result;
46305  ma_uint32 ilpf1;
46306  ma_uint32 ilpf2;
46307 
46308  if (pLPF == NULL) {
46309  return MA_INVALID_ARGS;
46310  }
46311 
46312  /* Faster path for in-place. */
46313  if (pFramesOut == pFramesIn) {
46314  for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46315  result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount);
46316  if (result != MA_SUCCESS) {
46317  return result;
46318  }
46319  }
46320 
46321  for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46322  result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount);
46323  if (result != MA_SUCCESS) {
46324  return result;
46325  }
46326  }
46327  }
46328 
46329  /* Slightly slower path for copying. */
46330  if (pFramesOut != pFramesIn) {
46331  ma_uint32 iFrame;
46332 
46333  /* */ if (pLPF->format == ma_format_f32) {
46334  /* */ float* pFramesOutF32 = ( float*)pFramesOut;
46335  const float* pFramesInF32 = (const float*)pFramesIn;
46336 
46337  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46338  ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);
46339  pFramesOutF32 += pLPF->channels;
46340  pFramesInF32 += pLPF->channels;
46341  }
46342  } else if (pLPF->format == ma_format_s16) {
46343  /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
46344  const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
46345 
46346  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46347  ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);
46348  pFramesOutS16 += pLPF->channels;
46349  pFramesInS16 += pLPF->channels;
46350  }
46351  } else {
46352  MA_ASSERT(MA_FALSE);
46353  return MA_INVALID_OPERATION; /* Should never hit this. */
46354  }
46355  }
46356 
46357  return MA_SUCCESS;
46358 }
46359 
46360 MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF)
46361 {
46362  if (pLPF == NULL) {
46363  return 0;
46364  }
46365 
46366  return pLPF->lpf2Count*2 + pLPF->lpf1Count;
46367 }
46368 
46369 
46370 /**************************************************************************************************************************************************************
46371 
46372 High-Pass Filtering
46373 
46374 **************************************************************************************************************************************************************/
46375 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
46376 {
46377  ma_hpf1_config config;
46378 
46379  MA_ZERO_OBJECT(&config);
46380  config.format = format;
46381  config.channels = channels;
46382  config.sampleRate = sampleRate;
46383  config.cutoffFrequency = cutoffFrequency;
46384 
46385  return config;
46386 }
46387 
46388 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
46389 {
46390  ma_hpf2_config config;
46391 
46392  MA_ZERO_OBJECT(&config);
46393  config.format = format;
46394  config.channels = channels;
46395  config.sampleRate = sampleRate;
46396  config.cutoffFrequency = cutoffFrequency;
46397  config.q = q;
46398 
46399  /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
46400  if (config.q == 0) {
46401  config.q = 0.707107;
46402  }
46403 
46404  return config;
46405 }
46406 
46407 
46408 typedef struct
46409 {
46410  size_t sizeInBytes;
46411  size_t r1Offset;
46412 } ma_hpf1_heap_layout;
46413 
46414 static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout)
46415 {
46416  MA_ASSERT(pHeapLayout != NULL);
46417 
46418  MA_ZERO_OBJECT(pHeapLayout);
46419 
46420  if (pConfig == NULL) {
46421  return MA_INVALID_ARGS;
46422  }
46423 
46424  if (pConfig->channels == 0) {
46425  return MA_INVALID_ARGS;
46426  }
46427 
46428  pHeapLayout->sizeInBytes = 0;
46429 
46430  /* R1 */
46431  pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
46432  pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
46433 
46434  /* Make sure allocation size is aligned. */
46435  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
46436 
46437  return MA_SUCCESS;
46438 }
46439 
46440 MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes)
46441 {
46442  ma_result result;
46443  ma_hpf1_heap_layout heapLayout;
46444 
46445  if (pHeapSizeInBytes == NULL) {
46446  return MA_INVALID_ARGS;
46447  }
46448 
46449  result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
46450  if (result != MA_SUCCESS) {
46451  return result;
46452  }
46453 
46454  *pHeapSizeInBytes = heapLayout.sizeInBytes;
46455 
46456  return MA_SUCCESS;
46457 }
46458 
46459 MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF)
46460 {
46461  ma_result result;
46462  ma_hpf1_heap_layout heapLayout;
46463 
46464  if (pLPF == NULL) {
46465  return MA_INVALID_ARGS;
46466  }
46467 
46468  MA_ZERO_OBJECT(pLPF);
46469 
46470  result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
46471  if (result != MA_SUCCESS) {
46472  return result;
46473  }
46474 
46475  pLPF->_pHeap = pHeap;
46476  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
46477 
46478  pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
46479 
46480  return ma_hpf1_reinit(pConfig, pLPF);
46481 }
46482 
46483 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF)
46484 {
46485  ma_result result;
46486  size_t heapSizeInBytes;
46487  void* pHeap;
46488 
46489  result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes);
46490  if (result != MA_SUCCESS) {
46491  return result;
46492  }
46493 
46494  if (heapSizeInBytes > 0) {
46495  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46496  if (pHeap == NULL) {
46497  return MA_OUT_OF_MEMORY;
46498  }
46499  } else {
46500  pHeap = NULL;
46501  }
46502 
46503  result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF);
46504  if (result != MA_SUCCESS) {
46505  ma_free(pHeap, pAllocationCallbacks);
46506  return result;
46507  }
46508 
46509  pLPF->_ownsHeap = MA_TRUE;
46510  return MA_SUCCESS;
46511 }
46512 
46513 MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
46514 {
46515  if (pHPF == NULL) {
46516  return;
46517  }
46518 
46519  if (pHPF->_ownsHeap) {
46520  ma_free(pHPF->_pHeap, pAllocationCallbacks);
46521  }
46522 }
46523 
46524 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
46525 {
46526  double a;
46527 
46528  if (pHPF == NULL || pConfig == NULL) {
46529  return MA_INVALID_ARGS;
46530  }
46531 
46532  /* Only supporting f32 and s16. */
46533  if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
46534  return MA_INVALID_ARGS;
46535  }
46536 
46537  /* The format cannot be changed after initialization. */
46538  if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
46539  return MA_INVALID_OPERATION;
46540  }
46541 
46542  /* The channel count cannot be changed after initialization. */
46543  if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
46544  return MA_INVALID_OPERATION;
46545  }
46546 
46547  pHPF->format = pConfig->format;
46548  pHPF->channels = pConfig->channels;
46549 
46550  a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
46551  if (pConfig->format == ma_format_f32) {
46552  pHPF->a.f32 = (float)a;
46553  } else {
46554  pHPF->a.s32 = ma_biquad_float_to_fp(a);
46555  }
46556 
46557  return MA_SUCCESS;
46558 }
46559 
46560 static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)
46561 {
46562  ma_uint32 c;
46563  const ma_uint32 channels = pHPF->channels;
46564  const float a = 1 - pHPF->a.f32;
46565  const float b = 1 - a;
46566 
46567  MA_ASSUME(channels > 0);
46568  for (c = 0; c < channels; c += 1) {
46569  float r1 = pHPF->pR1[c].f32;
46570  float x = pX[c];
46571  float y;
46572 
46573  y = b*x - a*r1;
46574 
46575  pY[c] = y;
46576  pHPF->pR1[c].f32 = y;
46577  }
46578 }
46579 
46580 static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)
46581 {
46582  ma_uint32 c;
46583  const ma_uint32 channels = pHPF->channels;
46584  const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);
46585  const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
46586 
46587  MA_ASSUME(channels > 0);
46588  for (c = 0; c < channels; c += 1) {
46589  ma_int32 r1 = pHPF->pR1[c].s32;
46590  ma_int32 x = pX[c];
46591  ma_int32 y;
46592 
46593  y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
46594 
46595  pY[c] = (ma_int16)y;
46596  pHPF->pR1[c].s32 = (ma_int32)y;
46597  }
46598 }
46599 
46600 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46601 {
46602  ma_uint32 n;
46603 
46604  if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
46605  return MA_INVALID_ARGS;
46606  }
46607 
46608  /* 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. */
46609 
46610  if (pHPF->format == ma_format_f32) {
46611  /* */ float* pY = ( float*)pFramesOut;
46612  const float* pX = (const float*)pFramesIn;
46613 
46614  for (n = 0; n < frameCount; n += 1) {
46615  ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);
46616  pY += pHPF->channels;
46617  pX += pHPF->channels;
46618  }
46619  } else if (pHPF->format == ma_format_s16) {
46620  /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
46621  const ma_int16* pX = (const ma_int16*)pFramesIn;
46622 
46623  for (n = 0; n < frameCount; n += 1) {
46624  ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);
46625  pY += pHPF->channels;
46626  pX += pHPF->channels;
46627  }
46628  } else {
46629  MA_ASSERT(MA_FALSE);
46630  return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
46631  }
46632 
46633  return MA_SUCCESS;
46634 }
46635 
46636 MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF)
46637 {
46638  if (pHPF == NULL) {
46639  return 0;
46640  }
46641 
46642  return 1;
46643 }
46644 
46645 
46646 static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)
46647 {
46648  ma_biquad_config bqConfig;
46649  double q;
46650  double w;
46651  double s;
46652  double c;
46653  double a;
46654 
46655  MA_ASSERT(pConfig != NULL);
46656 
46657  q = pConfig->q;
46658  w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
46659  s = ma_sind(w);
46660  c = ma_cosd(w);
46661  a = s / (2*q);
46662 
46663  bqConfig.b0 = (1 + c) / 2;
46664  bqConfig.b1 = -(1 + c);
46665  bqConfig.b2 = (1 + c) / 2;
46666  bqConfig.a0 = 1 + a;
46667  bqConfig.a1 = -2 * c;
46668  bqConfig.a2 = 1 - a;
46669 
46670  bqConfig.format = pConfig->format;
46671  bqConfig.channels = pConfig->channels;
46672 
46673  return bqConfig;
46674 }
46675 
46676 MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes)
46677 {
46678  ma_biquad_config bqConfig;
46679  bqConfig = ma_hpf2__get_biquad_config(pConfig);
46680 
46681  return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
46682 }
46683 
46684 MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF)
46685 {
46686  ma_result result;
46687  ma_biquad_config bqConfig;
46688 
46689  if (pHPF == NULL) {
46690  return MA_INVALID_ARGS;
46691  }
46692 
46693  MA_ZERO_OBJECT(pHPF);
46694 
46695  if (pConfig == NULL) {
46696  return MA_INVALID_ARGS;
46697  }
46698 
46699  bqConfig = ma_hpf2__get_biquad_config(pConfig);
46700  result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq);
46701  if (result != MA_SUCCESS) {
46702  return result;
46703  }
46704 
46705  return MA_SUCCESS;
46706 }
46707 
46708 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF)
46709 {
46710  ma_result result;
46711  size_t heapSizeInBytes;
46712  void* pHeap;
46713 
46714  result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes);
46715  if (result != MA_SUCCESS) {
46716  return result;
46717  }
46718 
46719  if (heapSizeInBytes > 0) {
46720  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46721  if (pHeap == NULL) {
46722  return MA_OUT_OF_MEMORY;
46723  }
46724  } else {
46725  pHeap = NULL;
46726  }
46727 
46728  result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF);
46729  if (result != MA_SUCCESS) {
46730  ma_free(pHeap, pAllocationCallbacks);
46731  return result;
46732  }
46733 
46734  pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
46735  return MA_SUCCESS;
46736 }
46737 
46738 MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
46739 {
46740  if (pHPF == NULL) {
46741  return;
46742  }
46743 
46744  ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
46745 }
46746 
46747 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
46748 {
46749  ma_result result;
46750  ma_biquad_config bqConfig;
46751 
46752  if (pHPF == NULL || pConfig == NULL) {
46753  return MA_INVALID_ARGS;
46754  }
46755 
46756  bqConfig = ma_hpf2__get_biquad_config(pConfig);
46757  result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
46758  if (result != MA_SUCCESS) {
46759  return result;
46760  }
46761 
46762  return MA_SUCCESS;
46763 }
46764 
46765 static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
46766 {
46767  ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);
46768 }
46769 
46770 static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)
46771 {
46772  ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);
46773 }
46774 
46775 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46776 {
46777  if (pHPF == NULL) {
46778  return MA_INVALID_ARGS;
46779  }
46780 
46781  return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
46782 }
46783 
46784 MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF)
46785 {
46786  if (pHPF == NULL) {
46787  return 0;
46788  }
46789 
46790  return ma_biquad_get_latency(&pHPF->bq);
46791 }
46792 
46793 
46794 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
46795 {
46796  ma_hpf_config config;
46797 
46798  MA_ZERO_OBJECT(&config);
46799  config.format = format;
46800  config.channels = channels;
46801  config.sampleRate = sampleRate;
46802  config.cutoffFrequency = cutoffFrequency;
46803  config.order = ma_min(order, MA_MAX_FILTER_ORDER);
46804 
46805  return config;
46806 }
46807 
46808 
46809 typedef struct
46810 {
46811  size_t sizeInBytes;
46812  size_t hpf1Offset;
46813  size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */
46814 } ma_hpf_heap_layout;
46815 
46816 static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count)
46817 {
46818  MA_ASSERT(pHPF1Count != NULL);
46819  MA_ASSERT(pHPF2Count != NULL);
46820 
46821  *pHPF1Count = order % 2;
46822  *pHPF2Count = order / 2;
46823 }
46824 
46825 static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout)
46826 {
46827  ma_result result;
46828  ma_uint32 hpf1Count;
46829  ma_uint32 hpf2Count;
46830  ma_uint32 ihpf1;
46831  ma_uint32 ihpf2;
46832 
46833  MA_ASSERT(pHeapLayout != NULL);
46834 
46835  MA_ZERO_OBJECT(pHeapLayout);
46836 
46837  if (pConfig == NULL) {
46838  return MA_INVALID_ARGS;
46839  }
46840 
46841  if (pConfig->channels == 0) {
46842  return MA_INVALID_ARGS;
46843  }
46844 
46845  if (pConfig->order > MA_MAX_FILTER_ORDER) {
46846  return MA_INVALID_ARGS;
46847  }
46848 
46849  ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
46850 
46851  pHeapLayout->sizeInBytes = 0;
46852 
46853  /* HPF 1 */
46854  pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes;
46855  for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
46856  size_t hpf1HeapSizeInBytes;
46857  ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
46858 
46859  result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
46860  if (result != MA_SUCCESS) {
46861  return result;
46862  }
46863 
46864  pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes;
46865  }
46866 
46867  /* HPF 2*/
46868  pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes;
46869  for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
46870  size_t hpf2HeapSizeInBytes;
46871  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. */
46872 
46873  result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
46874  if (result != MA_SUCCESS) {
46875  return result;
46876  }
46877 
46878  pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes;
46879  }
46880 
46881  /* Make sure allocation size is aligned. */
46882  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
46883 
46884  return MA_SUCCESS;
46885 }
46886 
46887 static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew)
46888 {
46889  ma_result result;
46890  ma_uint32 hpf1Count;
46891  ma_uint32 hpf2Count;
46892  ma_uint32 ihpf1;
46893  ma_uint32 ihpf2;
46894  ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */
46895 
46896  if (pHPF == NULL || pConfig == NULL) {
46897  return MA_INVALID_ARGS;
46898  }
46899 
46900  /* Only supporting f32 and s16. */
46901  if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
46902  return MA_INVALID_ARGS;
46903  }
46904 
46905  /* The format cannot be changed after initialization. */
46906  if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
46907  return MA_INVALID_OPERATION;
46908  }
46909 
46910  /* The channel count cannot be changed after initialization. */
46911  if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
46912  return MA_INVALID_OPERATION;
46913  }
46914 
46915  if (pConfig->order > MA_MAX_FILTER_ORDER) {
46916  return MA_INVALID_ARGS;
46917  }
46918 
46919  ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
46920 
46921  /* The filter order can't change between reinits. */
46922  if (!isNew) {
46923  if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {
46924  return MA_INVALID_OPERATION;
46925  }
46926  }
46927 
46928  if (isNew) {
46929  result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
46930  if (result != MA_SUCCESS) {
46931  return result;
46932  }
46933 
46934  pHPF->_pHeap = pHeap;
46935  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
46936 
46937  pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset);
46938  pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset);
46939  } else {
46940  MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */
46941  }
46942 
46943  for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
46944  ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
46945 
46946  if (isNew) {
46947  size_t hpf1HeapSizeInBytes;
46948 
46949  result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
46950  if (result == MA_SUCCESS) {
46951  result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]);
46952  }
46953  } else {
46954  result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]);
46955  }
46956 
46957  if (result != MA_SUCCESS) {
46958  ma_uint32 jhpf1;
46959 
46960  for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) {
46961  ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46962  }
46963 
46964  return result;
46965  }
46966  }
46967 
46968  for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
46969  ma_hpf2_config hpf2Config;
46970  double q;
46971  double a;
46972 
46973  /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
46974  if (hpf1Count == 1) {
46975  a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
46976  } else {
46977  a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
46978  }
46979  q = 1 / (2*ma_cosd(a));
46980 
46981  hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
46982 
46983  if (isNew) {
46984  size_t hpf2HeapSizeInBytes;
46985 
46986  result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
46987  if (result == MA_SUCCESS) {
46988  result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]);
46989  }
46990  } else {
46991  result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]);
46992  }
46993 
46994  if (result != MA_SUCCESS) {
46995  ma_uint32 jhpf1;
46996  ma_uint32 jhpf2;
46997 
46998  for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) {
46999  ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
47000  }
47001 
47002  for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) {
47003  ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
47004  }
47005 
47006  return result;
47007  }
47008  }
47009 
47010  pHPF->hpf1Count = hpf1Count;
47011  pHPF->hpf2Count = hpf2Count;
47012  pHPF->format = pConfig->format;
47013  pHPF->channels = pConfig->channels;
47014  pHPF->sampleRate = pConfig->sampleRate;
47015 
47016  return MA_SUCCESS;
47017 }
47018 
47019 MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes)
47020 {
47021  ma_result result;
47022  ma_hpf_heap_layout heapLayout;
47023 
47024  if (pHeapSizeInBytes == NULL) {
47025  return MA_INVALID_ARGS;
47026  }
47027 
47028  *pHeapSizeInBytes = 0;
47029 
47030  result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
47031  if (result != MA_SUCCESS) {
47032  return result;
47033  }
47034 
47035  *pHeapSizeInBytes = heapLayout.sizeInBytes;
47036 
47037  return result;
47038 }
47039 
47040 MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF)
47041 {
47042  if (pLPF == NULL) {
47043  return MA_INVALID_ARGS;
47044  }
47045 
47046  MA_ZERO_OBJECT(pLPF);
47047 
47048  return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);
47049 }
47050 
47051 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF)
47052 {
47053  ma_result result;
47054  size_t heapSizeInBytes;
47055  void* pHeap;
47056 
47057  result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes);
47058  if (result != MA_SUCCESS) {
47059  return result;
47060  }
47061 
47062  if (heapSizeInBytes > 0) {
47063  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47064  if (pHeap == NULL) {
47065  return MA_OUT_OF_MEMORY;
47066  }
47067  } else {
47068  pHeap = NULL;
47069  }
47070 
47071  result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF);
47072  if (result != MA_SUCCESS) {
47073  ma_free(pHeap, pAllocationCallbacks);
47074  return result;
47075  }
47076 
47077  pHPF->_ownsHeap = MA_TRUE;
47078  return MA_SUCCESS;
47079 }
47080 
47081 MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
47082 {
47083  ma_uint32 ihpf1;
47084  ma_uint32 ihpf2;
47085 
47086  if (pHPF == NULL) {
47087  return;
47088  }
47089 
47090  for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
47091  ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks);
47092  }
47093 
47094  for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
47095  ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks);
47096  }
47097 
47098  if (pHPF->_ownsHeap) {
47099  ma_free(pHPF->_pHeap, pAllocationCallbacks);
47100  }
47101 }
47102 
47103 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
47104 {
47105  return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE);
47106 }
47107 
47108 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47109 {
47110  ma_result result;
47111  ma_uint32 ihpf1;
47112  ma_uint32 ihpf2;
47113 
47114  if (pHPF == NULL) {
47115  return MA_INVALID_ARGS;
47116  }
47117 
47118  /* Faster path for in-place. */
47119  if (pFramesOut == pFramesIn) {
47120  for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
47121  result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount);
47122  if (result != MA_SUCCESS) {
47123  return result;
47124  }
47125  }
47126 
47127  for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
47128  result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount);
47129  if (result != MA_SUCCESS) {
47130  return result;
47131  }
47132  }
47133  }
47134 
47135  /* Slightly slower path for copying. */
47136  if (pFramesOut != pFramesIn) {
47137  ma_uint32 iFrame;
47138 
47139  /* */ if (pHPF->format == ma_format_f32) {
47140  /* */ float* pFramesOutF32 = ( float*)pFramesOut;
47141  const float* pFramesInF32 = (const float*)pFramesIn;
47142 
47143  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47144  MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
47145 
47146  for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
47147  ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32);
47148  }
47149 
47150  for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
47151  ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32);
47152  }
47153 
47154  pFramesOutF32 += pHPF->channels;
47155  pFramesInF32 += pHPF->channels;
47156  }
47157  } else if (pHPF->format == ma_format_s16) {
47158  /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
47159  const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
47160 
47161  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47162  MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
47163 
47164  for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
47165  ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16);
47166  }
47167 
47168  for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
47169  ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16);
47170  }
47171 
47172  pFramesOutS16 += pHPF->channels;
47173  pFramesInS16 += pHPF->channels;
47174  }
47175  } else {
47176  MA_ASSERT(MA_FALSE);
47177  return MA_INVALID_OPERATION; /* Should never hit this. */
47178  }
47179  }
47180 
47181  return MA_SUCCESS;
47182 }
47183 
47184 MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF)
47185 {
47186  if (pHPF == NULL) {
47187  return 0;
47188  }
47189 
47190  return pHPF->hpf2Count*2 + pHPF->hpf1Count;
47191 }
47192 
47193 
47194 /**************************************************************************************************************************************************************
47195 
47196 Band-Pass Filtering
47197 
47198 **************************************************************************************************************************************************************/
47199 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
47200 {
47201  ma_bpf2_config config;
47202 
47203  MA_ZERO_OBJECT(&config);
47204  config.format = format;
47205  config.channels = channels;
47206  config.sampleRate = sampleRate;
47207  config.cutoffFrequency = cutoffFrequency;
47208  config.q = q;
47209 
47210  /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
47211  if (config.q == 0) {
47212  config.q = 0.707107;
47213  }
47214 
47215  return config;
47216 }
47217 
47218 
47219 static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)
47220 {
47221  ma_biquad_config bqConfig;
47222  double q;
47223  double w;
47224  double s;
47225  double c;
47226  double a;
47227 
47228  MA_ASSERT(pConfig != NULL);
47229 
47230  q = pConfig->q;
47231  w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
47232  s = ma_sind(w);
47233  c = ma_cosd(w);
47234  a = s / (2*q);
47235 
47236  bqConfig.b0 = q * a;
47237  bqConfig.b1 = 0;
47238  bqConfig.b2 = -q * a;
47239  bqConfig.a0 = 1 + a;
47240  bqConfig.a1 = -2 * c;
47241  bqConfig.a2 = 1 - a;
47242 
47243  bqConfig.format = pConfig->format;
47244  bqConfig.channels = pConfig->channels;
47245 
47246  return bqConfig;
47247 }
47248 
47249 MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes)
47250 {
47251  ma_biquad_config bqConfig;
47252  bqConfig = ma_bpf2__get_biquad_config(pConfig);
47253 
47254  return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
47255 }
47256 
47257 MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF)
47258 {
47259  ma_result result;
47260  ma_biquad_config bqConfig;
47261 
47262  if (pBPF == NULL) {
47263  return MA_INVALID_ARGS;
47264  }
47265 
47266  MA_ZERO_OBJECT(pBPF);
47267 
47268  if (pConfig == NULL) {
47269  return MA_INVALID_ARGS;
47270  }
47271 
47272  bqConfig = ma_bpf2__get_biquad_config(pConfig);
47273  result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq);
47274  if (result != MA_SUCCESS) {
47275  return result;
47276  }
47277 
47278  return MA_SUCCESS;
47279 }
47280 
47281 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF)
47282 {
47283  ma_result result;
47284  size_t heapSizeInBytes;
47285  void* pHeap;
47286 
47287  result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes);
47288  if (result != MA_SUCCESS) {
47289  return result;
47290  }
47291 
47292  if (heapSizeInBytes > 0) {
47293  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47294  if (pHeap == NULL) {
47295  return MA_OUT_OF_MEMORY;
47296  }
47297  } else {
47298  pHeap = NULL;
47299  }
47300 
47301  result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF);
47302  if (result != MA_SUCCESS) {
47303  ma_free(pHeap, pAllocationCallbacks);
47304  return result;
47305  }
47306 
47307  pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
47308  return MA_SUCCESS;
47309 }
47310 
47311 MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
47312 {
47313  if (pBPF == NULL) {
47314  return;
47315  }
47316 
47317  ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
47318 }
47319 
47320 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
47321 {
47322  ma_result result;
47323  ma_biquad_config bqConfig;
47324 
47325  if (pBPF == NULL || pConfig == NULL) {
47326  return MA_INVALID_ARGS;
47327  }
47328 
47329  bqConfig = ma_bpf2__get_biquad_config(pConfig);
47330  result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
47331  if (result != MA_SUCCESS) {
47332  return result;
47333  }
47334 
47335  return MA_SUCCESS;
47336 }
47337 
47338 static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
47339 {
47340  ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);
47341 }
47342 
47343 static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)
47344 {
47345  ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);
47346 }
47347 
47348 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47349 {
47350  if (pBPF == NULL) {
47351  return MA_INVALID_ARGS;
47352  }
47353 
47354  return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
47355 }
47356 
47357 MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF)
47358 {
47359  if (pBPF == NULL) {
47360  return 0;
47361  }
47362 
47363  return ma_biquad_get_latency(&pBPF->bq);
47364 }
47365 
47366 
47367 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
47368 {
47369  ma_bpf_config config;
47370 
47371  MA_ZERO_OBJECT(&config);
47372  config.format = format;
47373  config.channels = channels;
47374  config.sampleRate = sampleRate;
47375  config.cutoffFrequency = cutoffFrequency;
47376  config.order = ma_min(order, MA_MAX_FILTER_ORDER);
47377 
47378  return config;
47379 }
47380 
47381 
47382 typedef struct
47383 {
47384  size_t sizeInBytes;
47385  size_t bpf2Offset;
47386 } ma_bpf_heap_layout;
47387 
47388 static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout)
47389 {
47390  ma_result result;
47391  ma_uint32 bpf2Count;
47392  ma_uint32 ibpf2;
47393 
47394  MA_ASSERT(pHeapLayout != NULL);
47395 
47396  MA_ZERO_OBJECT(pHeapLayout);
47397 
47398  if (pConfig == NULL) {
47399  return MA_INVALID_ARGS;
47400  }
47401 
47402  if (pConfig->order > MA_MAX_FILTER_ORDER) {
47403  return MA_INVALID_ARGS;
47404  }
47405 
47406  /* We must have an even number of order. */
47407  if ((pConfig->order & 0x1) != 0) {
47408  return MA_INVALID_ARGS;
47409  }
47410 
47411  bpf2Count = pConfig->channels / 2;
47412 
47413  pHeapLayout->sizeInBytes = 0;
47414 
47415  /* BPF 2 */
47416  pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes;
47417  for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
47418  size_t bpf2HeapSizeInBytes;
47419  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. */
47420 
47421  result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
47422  if (result != MA_SUCCESS) {
47423  return result;
47424  }
47425 
47426  pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes;
47427  }
47428 
47429  /* Make sure allocation size is aligned. */
47430  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
47431 
47432  return MA_SUCCESS;
47433 }
47434 
47435 static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew)
47436 {
47437  ma_result result;
47438  ma_uint32 bpf2Count;
47439  ma_uint32 ibpf2;
47440  ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */
47441 
47442  if (pBPF == NULL || pConfig == NULL) {
47443  return MA_INVALID_ARGS;
47444  }
47445 
47446  /* Only supporting f32 and s16. */
47447  if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
47448  return MA_INVALID_ARGS;
47449  }
47450 
47451  /* The format cannot be changed after initialization. */
47452  if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {
47453  return MA_INVALID_OPERATION;
47454  }
47455 
47456  /* The channel count cannot be changed after initialization. */
47457  if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {
47458  return MA_INVALID_OPERATION;
47459  }
47460 
47461  if (pConfig->order > MA_MAX_FILTER_ORDER) {
47462  return MA_INVALID_ARGS;
47463  }
47464 
47465  /* We must have an even number of order. */
47466  if ((pConfig->order & 0x1) != 0) {
47467  return MA_INVALID_ARGS;
47468  }
47469 
47470  bpf2Count = pConfig->order / 2;
47471 
47472  /* The filter order can't change between reinits. */
47473  if (!isNew) {
47474  if (pBPF->bpf2Count != bpf2Count) {
47475  return MA_INVALID_OPERATION;
47476  }
47477  }
47478 
47479  if (isNew) {
47480  result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
47481  if (result != MA_SUCCESS) {
47482  return result;
47483  }
47484 
47485  pBPF->_pHeap = pHeap;
47486  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
47487 
47488  pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset);
47489  } else {
47490  MA_ZERO_OBJECT(&heapLayout);
47491  }
47492 
47493  for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
47494  ma_bpf2_config bpf2Config;
47495  double q;
47496 
47497  /* TODO: Calculate Q to make this a proper Butterworth filter. */
47498  q = 0.707107;
47499 
47500  bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
47501 
47502  if (isNew) {
47503  size_t bpf2HeapSizeInBytes;
47504 
47505  result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
47506  if (result == MA_SUCCESS) {
47507  result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]);
47508  }
47509  } else {
47510  result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]);
47511  }
47512 
47513  if (result != MA_SUCCESS) {
47514  return result;
47515  }
47516  }
47517 
47518  pBPF->bpf2Count = bpf2Count;
47519  pBPF->format = pConfig->format;
47520  pBPF->channels = pConfig->channels;
47521 
47522  return MA_SUCCESS;
47523 }
47524 
47525 
47526 MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes)
47527 {
47528  ma_result result;
47529  ma_bpf_heap_layout heapLayout;
47530 
47531  if (pHeapSizeInBytes == NULL) {
47532  return MA_INVALID_ARGS;
47533  }
47534 
47535  *pHeapSizeInBytes = 0;
47536 
47537  result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
47538  if (result != MA_SUCCESS) {
47539  return result;
47540  }
47541 
47542  *pHeapSizeInBytes = heapLayout.sizeInBytes;
47543 
47544  return MA_SUCCESS;
47545 }
47546 
47547 MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF)
47548 {
47549  if (pBPF == NULL) {
47550  return MA_INVALID_ARGS;
47551  }
47552 
47553  MA_ZERO_OBJECT(pBPF);
47554 
47555  return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE);
47556 }
47557 
47558 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF)
47559 {
47560  ma_result result;
47561  size_t heapSizeInBytes;
47562  void* pHeap;
47563 
47564  result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes);
47565  if (result != MA_SUCCESS) {
47566  return result;
47567  }
47568 
47569  if (heapSizeInBytes > 0) {
47570  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47571  if (pHeap == NULL) {
47572  return MA_OUT_OF_MEMORY;
47573  }
47574  } else {
47575  pHeap = NULL;
47576  }
47577 
47578  result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF);
47579  if (result != MA_SUCCESS) {
47580  ma_free(pHeap, pAllocationCallbacks);
47581  return result;
47582  }
47583 
47584  pBPF->_ownsHeap = MA_TRUE;
47585  return MA_SUCCESS;
47586 }
47587 
47588 MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
47589 {
47590  ma_uint32 ibpf2;
47591 
47592  if (pBPF == NULL) {
47593  return;
47594  }
47595 
47596  for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
47597  ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks);
47598  }
47599 
47600  if (pBPF->_ownsHeap) {
47601  ma_free(pBPF->_pHeap, pAllocationCallbacks);
47602  }
47603 }
47604 
47605 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
47606 {
47607  return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE);
47608 }
47609 
47610 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47611 {
47612  ma_result result;
47613  ma_uint32 ibpf2;
47614 
47615  if (pBPF == NULL) {
47616  return MA_INVALID_ARGS;
47617  }
47618 
47619  /* Faster path for in-place. */
47620  if (pFramesOut == pFramesIn) {
47621  for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
47622  result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount);
47623  if (result != MA_SUCCESS) {
47624  return result;
47625  }
47626  }
47627  }
47628 
47629  /* Slightly slower path for copying. */
47630  if (pFramesOut != pFramesIn) {
47631  ma_uint32 iFrame;
47632 
47633  /* */ if (pBPF->format == ma_format_f32) {
47634  /* */ float* pFramesOutF32 = ( float*)pFramesOut;
47635  const float* pFramesInF32 = (const float*)pFramesIn;
47636 
47637  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47638  MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
47639 
47640  for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
47641  ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32);
47642  }
47643 
47644  pFramesOutF32 += pBPF->channels;
47645  pFramesInF32 += pBPF->channels;
47646  }
47647  } else if (pBPF->format == ma_format_s16) {
47648  /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
47649  const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
47650 
47651  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47652  MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
47653 
47654  for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
47655  ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16);
47656  }
47657 
47658  pFramesOutS16 += pBPF->channels;
47659  pFramesInS16 += pBPF->channels;
47660  }
47661  } else {
47662  MA_ASSERT(MA_FALSE);
47663  return MA_INVALID_OPERATION; /* Should never hit this. */
47664  }
47665  }
47666 
47667  return MA_SUCCESS;
47668 }
47669 
47670 MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF)
47671 {
47672  if (pBPF == NULL) {
47673  return 0;
47674  }
47675 
47676  return pBPF->bpf2Count*2;
47677 }
47678 
47679 
47680 /**************************************************************************************************************************************************************
47681 
47682 Notching Filter
47683 
47684 **************************************************************************************************************************************************************/
47685 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
47686 {
47687  ma_notch2_config config;
47688 
47689  MA_ZERO_OBJECT(&config);
47690  config.format = format;
47691  config.channels = channels;
47692  config.sampleRate = sampleRate;
47693  config.q = q;
47694  config.frequency = frequency;
47695 
47696  if (config.q == 0) {
47697  config.q = 0.707107;
47698  }
47699 
47700  return config;
47701 }
47702 
47703 
47704 static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)
47705 {
47706  ma_biquad_config bqConfig;
47707  double q;
47708  double w;
47709  double s;
47710  double c;
47711  double a;
47712 
47713  MA_ASSERT(pConfig != NULL);
47714 
47715  q = pConfig->q;
47716  w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
47717  s = ma_sind(w);
47718  c = ma_cosd(w);
47719  a = s / (2*q);
47720 
47721  bqConfig.b0 = 1;
47722  bqConfig.b1 = -2 * c;
47723  bqConfig.b2 = 1;
47724  bqConfig.a0 = 1 + a;
47725  bqConfig.a1 = -2 * c;
47726  bqConfig.a2 = 1 - a;
47727 
47728  bqConfig.format = pConfig->format;
47729  bqConfig.channels = pConfig->channels;
47730 
47731  return bqConfig;
47732 }
47733 
47734 MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes)
47735 {
47736  ma_biquad_config bqConfig;
47737  bqConfig = ma_notch2__get_biquad_config(pConfig);
47738 
47739  return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
47740 }
47741 
47742 MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter)
47743 {
47744  ma_result result;
47745  ma_biquad_config bqConfig;
47746 
47747  if (pFilter == NULL) {
47748  return MA_INVALID_ARGS;
47749  }
47750 
47751  MA_ZERO_OBJECT(pFilter);
47752 
47753  if (pConfig == NULL) {
47754  return MA_INVALID_ARGS;
47755  }
47756 
47757  bqConfig = ma_notch2__get_biquad_config(pConfig);
47758  result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
47759  if (result != MA_SUCCESS) {
47760  return result;
47761  }
47762 
47763  return MA_SUCCESS;
47764 }
47765 
47766 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter)
47767 {
47768  ma_result result;
47769  size_t heapSizeInBytes;
47770  void* pHeap;
47771 
47772  result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes);
47773  if (result != MA_SUCCESS) {
47774  return result;
47775  }
47776 
47777  if (heapSizeInBytes > 0) {
47778  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47779  if (pHeap == NULL) {
47780  return MA_OUT_OF_MEMORY;
47781  }
47782  } else {
47783  pHeap = NULL;
47784  }
47785 
47786  result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter);
47787  if (result != MA_SUCCESS) {
47788  ma_free(pHeap, pAllocationCallbacks);
47789  return result;
47790  }
47791 
47792  pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
47793  return MA_SUCCESS;
47794 }
47795 
47796 MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
47797 {
47798  if (pFilter == NULL) {
47799  return;
47800  }
47801 
47802  ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
47803 }
47804 
47805 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter)
47806 {
47807  ma_result result;
47808  ma_biquad_config bqConfig;
47809 
47810  if (pFilter == NULL || pConfig == NULL) {
47811  return MA_INVALID_ARGS;
47812  }
47813 
47814  bqConfig = ma_notch2__get_biquad_config(pConfig);
47815  result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
47816  if (result != MA_SUCCESS) {
47817  return result;
47818  }
47819 
47820  return MA_SUCCESS;
47821 }
47822 
47823 static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
47824 {
47825  ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
47826 }
47827 
47828 static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)
47829 {
47830  ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
47831 }
47832 
47833 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47834 {
47835  if (pFilter == NULL) {
47836  return MA_INVALID_ARGS;
47837  }
47838 
47839  return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
47840 }
47841 
47842 MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter)
47843 {
47844  if (pFilter == NULL) {
47845  return 0;
47846  }
47847 
47848  return ma_biquad_get_latency(&pFilter->bq);
47849 }
47850 
47851 
47852 
47853 /**************************************************************************************************************************************************************
47854 
47855 Peaking EQ Filter
47856 
47857 **************************************************************************************************************************************************************/
47858 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
47859 {
47860  ma_peak2_config config;
47861 
47862  MA_ZERO_OBJECT(&config);
47863  config.format = format;
47864  config.channels = channels;
47865  config.sampleRate = sampleRate;
47866  config.gainDB = gainDB;
47867  config.q = q;
47868  config.frequency = frequency;
47869 
47870  if (config.q == 0) {
47871  config.q = 0.707107;
47872  }
47873 
47874  return config;
47875 }
47876 
47877 
47878 static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
47879 {
47880  ma_biquad_config bqConfig;
47881  double q;
47882  double w;
47883  double s;
47884  double c;
47885  double a;
47886  double A;
47887 
47888  MA_ASSERT(pConfig != NULL);
47889 
47890  q = pConfig->q;
47891  w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
47892  s = ma_sind(w);
47893  c = ma_cosd(w);
47894  a = s / (2*q);
47895  A = ma_powd(10, (pConfig->gainDB / 40));
47896 
47897  bqConfig.b0 = 1 + (a * A);
47898  bqConfig.b1 = -2 * c;
47899  bqConfig.b2 = 1 - (a * A);
47900  bqConfig.a0 = 1 + (a / A);
47901  bqConfig.a1 = -2 * c;
47902  bqConfig.a2 = 1 - (a / A);
47903 
47904  bqConfig.format = pConfig->format;
47905  bqConfig.channels = pConfig->channels;
47906 
47907  return bqConfig;
47908 }
47909 
47910 MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes)
47911 {
47912  ma_biquad_config bqConfig;
47913  bqConfig = ma_peak2__get_biquad_config(pConfig);
47914 
47915  return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
47916 }
47917 
47918 MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter)
47919 {
47920  ma_result result;
47921  ma_biquad_config bqConfig;
47922 
47923  if (pFilter == NULL) {
47924  return MA_INVALID_ARGS;
47925  }
47926 
47927  MA_ZERO_OBJECT(pFilter);
47928 
47929  if (pConfig == NULL) {
47930  return MA_INVALID_ARGS;
47931  }
47932 
47933  bqConfig = ma_peak2__get_biquad_config(pConfig);
47934  result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
47935  if (result != MA_SUCCESS) {
47936  return result;
47937  }
47938 
47939  return MA_SUCCESS;
47940 }
47941 
47942 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter)
47943 {
47944  ma_result result;
47945  size_t heapSizeInBytes;
47946  void* pHeap;
47947 
47948  result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes);
47949  if (result != MA_SUCCESS) {
47950  return result;
47951  }
47952 
47953  if (heapSizeInBytes > 0) {
47954  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47955  if (pHeap == NULL) {
47956  return MA_OUT_OF_MEMORY;
47957  }
47958  } else {
47959  pHeap = NULL;
47960  }
47961 
47962  result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter);
47963  if (result != MA_SUCCESS) {
47964  ma_free(pHeap, pAllocationCallbacks);
47965  return result;
47966  }
47967 
47968  pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
47969  return MA_SUCCESS;
47970 }
47971 
47972 MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
47973 {
47974  if (pFilter == NULL) {
47975  return;
47976  }
47977 
47978  ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
47979 }
47980 
47981 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter)
47982 {
47983  ma_result result;
47984  ma_biquad_config bqConfig;
47985 
47986  if (pFilter == NULL || pConfig == NULL) {
47987  return MA_INVALID_ARGS;
47988  }
47989 
47990  bqConfig = ma_peak2__get_biquad_config(pConfig);
47991  result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
47992  if (result != MA_SUCCESS) {
47993  return result;
47994  }
47995 
47996  return MA_SUCCESS;
47997 }
47998 
47999 static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
48000 {
48001  ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
48002 }
48003 
48004 static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
48005 {
48006  ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
48007 }
48008 
48009 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48010 {
48011  if (pFilter == NULL) {
48012  return MA_INVALID_ARGS;
48013  }
48014 
48015  return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
48016 }
48017 
48018 MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter)
48019 {
48020  if (pFilter == NULL) {
48021  return 0;
48022  }
48023 
48024  return ma_biquad_get_latency(&pFilter->bq);
48025 }
48026 
48027 
48028 /**************************************************************************************************************************************************************
48029 
48030 Low Shelf Filter
48031 
48032 **************************************************************************************************************************************************************/
48033 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
48034 {
48035  ma_loshelf2_config config;
48036 
48037  MA_ZERO_OBJECT(&config);
48038  config.format = format;
48039  config.channels = channels;
48040  config.sampleRate = sampleRate;
48041  config.gainDB = gainDB;
48042  config.shelfSlope = shelfSlope;
48043  config.frequency = frequency;
48044 
48045  return config;
48046 }
48047 
48048 
48049 static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)
48050 {
48051  ma_biquad_config bqConfig;
48052  double w;
48053  double s;
48054  double c;
48055  double A;
48056  double S;
48057  double a;
48058  double sqrtA;
48059 
48060  MA_ASSERT(pConfig != NULL);
48061 
48062  w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
48063  s = ma_sind(w);
48064  c = ma_cosd(w);
48065  A = ma_powd(10, (pConfig->gainDB / 40));
48066  S = pConfig->shelfSlope;
48067  a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
48068  sqrtA = 2*ma_sqrtd(A)*a;
48069 
48070  bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA);
48071  bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c);
48072  bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA);
48073  bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA;
48074  bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);
48075  bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA;
48076 
48077  bqConfig.format = pConfig->format;
48078  bqConfig.channels = pConfig->channels;
48079 
48080  return bqConfig;
48081 }
48082 
48083 MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes)
48084 {
48085  ma_biquad_config bqConfig;
48086  bqConfig = ma_loshelf2__get_biquad_config(pConfig);
48087 
48088  return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
48089 }
48090 
48091 MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter)
48092 {
48093  ma_result result;
48094  ma_biquad_config bqConfig;
48095 
48096  if (pFilter == NULL) {
48097  return MA_INVALID_ARGS;
48098  }
48099 
48100  MA_ZERO_OBJECT(pFilter);
48101 
48102  if (pConfig == NULL) {
48103  return MA_INVALID_ARGS;
48104  }
48105 
48106  bqConfig = ma_loshelf2__get_biquad_config(pConfig);
48107  result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
48108  if (result != MA_SUCCESS) {
48109  return result;
48110  }
48111 
48112  return MA_SUCCESS;
48113 }
48114 
48115 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter)
48116 {
48117  ma_result result;
48118  size_t heapSizeInBytes;
48119  void* pHeap;
48120 
48121  result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes);
48122  if (result != MA_SUCCESS) {
48123  return result;
48124  }
48125 
48126  if (heapSizeInBytes > 0) {
48127  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48128  if (pHeap == NULL) {
48129  return MA_OUT_OF_MEMORY;
48130  }
48131  } else {
48132  pHeap = NULL;
48133  }
48134 
48135  result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter);
48136  if (result != MA_SUCCESS) {
48137  ma_free(pHeap, pAllocationCallbacks);
48138  return result;
48139  }
48140 
48141  pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
48142  return MA_SUCCESS;
48143 }
48144 
48145 MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
48146 {
48147  if (pFilter == NULL) {
48148  return;
48149  }
48150 
48151  ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
48152 }
48153 
48154 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
48155 {
48156  ma_result result;
48157  ma_biquad_config bqConfig;
48158 
48159  if (pFilter == NULL || pConfig == NULL) {
48160  return MA_INVALID_ARGS;
48161  }
48162 
48163  bqConfig = ma_loshelf2__get_biquad_config(pConfig);
48164  result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
48165  if (result != MA_SUCCESS) {
48166  return result;
48167  }
48168 
48169  return MA_SUCCESS;
48170 }
48171 
48172 static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
48173 {
48174  ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
48175 }
48176 
48177 static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)
48178 {
48179  ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
48180 }
48181 
48182 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48183 {
48184  if (pFilter == NULL) {
48185  return MA_INVALID_ARGS;
48186  }
48187 
48188  return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
48189 }
48190 
48191 MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter)
48192 {
48193  if (pFilter == NULL) {
48194  return 0;
48195  }
48196 
48197  return ma_biquad_get_latency(&pFilter->bq);
48198 }
48199 
48200 
48201 /**************************************************************************************************************************************************************
48202 
48203 High Shelf Filter
48204 
48205 **************************************************************************************************************************************************************/
48206 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
48207 {
48208  ma_hishelf2_config config;
48209 
48210  MA_ZERO_OBJECT(&config);
48211  config.format = format;
48212  config.channels = channels;
48213  config.sampleRate = sampleRate;
48214  config.gainDB = gainDB;
48215  config.shelfSlope = shelfSlope;
48216  config.frequency = frequency;
48217 
48218  return config;
48219 }
48220 
48221 
48222 static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)
48223 {
48224  ma_biquad_config bqConfig;
48225  double w;
48226  double s;
48227  double c;
48228  double A;
48229  double S;
48230  double a;
48231  double sqrtA;
48232 
48233  MA_ASSERT(pConfig != NULL);
48234 
48235  w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
48236  s = ma_sind(w);
48237  c = ma_cosd(w);
48238  A = ma_powd(10, (pConfig->gainDB / 40));
48239  S = pConfig->shelfSlope;
48240  a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
48241  sqrtA = 2*ma_sqrtd(A)*a;
48242 
48243  bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA);
48244  bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);
48245  bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA);
48246  bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA;
48247  bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c);
48248  bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA;
48249 
48250  bqConfig.format = pConfig->format;
48251  bqConfig.channels = pConfig->channels;
48252 
48253  return bqConfig;
48254 }
48255 
48256 MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes)
48257 {
48258  ma_biquad_config bqConfig;
48259  bqConfig = ma_hishelf2__get_biquad_config(pConfig);
48260 
48261  return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
48262 }
48263 
48264 MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter)
48265 {
48266  ma_result result;
48267  ma_biquad_config bqConfig;
48268 
48269  if (pFilter == NULL) {
48270  return MA_INVALID_ARGS;
48271  }
48272 
48273  MA_ZERO_OBJECT(pFilter);
48274 
48275  if (pConfig == NULL) {
48276  return MA_INVALID_ARGS;
48277  }
48278 
48279  bqConfig = ma_hishelf2__get_biquad_config(pConfig);
48280  result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
48281  if (result != MA_SUCCESS) {
48282  return result;
48283  }
48284 
48285  return MA_SUCCESS;
48286 }
48287 
48288 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter)
48289 {
48290  ma_result result;
48291  size_t heapSizeInBytes;
48292  void* pHeap;
48293 
48294  result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes);
48295  if (result != MA_SUCCESS) {
48296  return result;
48297  }
48298 
48299  if (heapSizeInBytes > 0) {
48300  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48301  if (pHeap == NULL) {
48302  return MA_OUT_OF_MEMORY;
48303  }
48304  } else {
48305  pHeap = NULL;
48306  }
48307 
48308  result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter);
48309  if (result != MA_SUCCESS) {
48310  ma_free(pHeap, pAllocationCallbacks);
48311  return result;
48312  }
48313 
48314  pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
48315  return MA_SUCCESS;
48316 }
48317 
48318 MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
48319 {
48320  if (pFilter == NULL) {
48321  return;
48322  }
48323 
48324  ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
48325 }
48326 
48327 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
48328 {
48329  ma_result result;
48330  ma_biquad_config bqConfig;
48331 
48332  if (pFilter == NULL || pConfig == NULL) {
48333  return MA_INVALID_ARGS;
48334  }
48335 
48336  bqConfig = ma_hishelf2__get_biquad_config(pConfig);
48337  result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
48338  if (result != MA_SUCCESS) {
48339  return result;
48340  }
48341 
48342  return MA_SUCCESS;
48343 }
48344 
48345 static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
48346 {
48347  ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
48348 }
48349 
48350 static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)
48351 {
48352  ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
48353 }
48354 
48355 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48356 {
48357  if (pFilter == NULL) {
48358  return MA_INVALID_ARGS;
48359  }
48360 
48361  return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
48362 }
48363 
48364 MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter)
48365 {
48366  if (pFilter == NULL) {
48367  return 0;
48368  }
48369 
48370  return ma_biquad_get_latency(&pFilter->bq);
48371 }
48372 
48373 
48374 
48375 /*
48376 Delay
48377 */
48378 MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
48379 {
48380  ma_delay_config config;
48381 
48382  MA_ZERO_OBJECT(&config);
48383  config.channels = channels;
48384  config.sampleRate = sampleRate;
48385  config.delayInFrames = delayInFrames;
48386  config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */
48387  config.wet = 1;
48388  config.dry = 1;
48389  config.decay = decay;
48390 
48391  return config;
48392 }
48393 
48394 
48395 MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay)
48396 {
48397  if (pDelay == NULL) {
48398  return MA_INVALID_ARGS;
48399  }
48400 
48401  MA_ZERO_OBJECT(pDelay);
48402 
48403  if (pConfig == NULL) {
48404  return MA_INVALID_ARGS;
48405  }
48406 
48407  if (pConfig->decay < 0 || pConfig->decay > 1) {
48408  return MA_INVALID_ARGS;
48409  }
48410 
48411  pDelay->config = *pConfig;
48412  pDelay->bufferSizeInFrames = pConfig->delayInFrames;
48413  pDelay->cursor = 0;
48414 
48415  pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks);
48416  if (pDelay->pBuffer == NULL) {
48417  return MA_OUT_OF_MEMORY;
48418  }
48419 
48420  ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels);
48421 
48422  return MA_SUCCESS;
48423 }
48424 
48425 MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks)
48426 {
48427  if (pDelay == NULL) {
48428  return;
48429  }
48430 
48431  ma_free(pDelay->pBuffer, pAllocationCallbacks);
48432 }
48433 
48434 MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
48435 {
48436  ma_uint32 iFrame;
48437  ma_uint32 iChannel;
48438  float* pFramesOutF32 = (float*)pFramesOut;
48439  const float* pFramesInF32 = (const float*)pFramesIn;
48440 
48441  if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) {
48442  return MA_INVALID_ARGS;
48443  }
48444 
48445  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48446  for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) {
48447  ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel;
48448 
48449  if (pDelay->config.delayStart) {
48450  /* Delayed start. */
48451 
48452  /* Read */
48453  pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
48454 
48455  /* Feedback */
48456  pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
48457  } else {
48458  /* Immediate start */
48459 
48460  /* Feedback */
48461  pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
48462 
48463  /* Read */
48464  pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
48465  }
48466  }
48467 
48468  pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames;
48469 
48470  pFramesOutF32 += pDelay->config.channels;
48471  pFramesInF32 += pDelay->config.channels;
48472  }
48473 
48474  return MA_SUCCESS;
48475 }
48476 
48477 MA_API void ma_delay_set_wet(ma_delay* pDelay, float value)
48478 {
48479  if (pDelay == NULL) {
48480  return;
48481  }
48482 
48483  pDelay->config.wet = value;
48484 }
48485 
48486 MA_API float ma_delay_get_wet(const ma_delay* pDelay)
48487 {
48488  if (pDelay == NULL) {
48489  return 0;
48490  }
48491 
48492  return pDelay->config.wet;
48493 }
48494 
48495 MA_API void ma_delay_set_dry(ma_delay* pDelay, float value)
48496 {
48497  if (pDelay == NULL) {
48498  return;
48499  }
48500 
48501  pDelay->config.dry = value;
48502 }
48503 
48504 MA_API float ma_delay_get_dry(const ma_delay* pDelay)
48505 {
48506  if (pDelay == NULL) {
48507  return 0;
48508  }
48509 
48510  return pDelay->config.dry;
48511 }
48512 
48513 MA_API void ma_delay_set_decay(ma_delay* pDelay, float value)
48514 {
48515  if (pDelay == NULL) {
48516  return;
48517  }
48518 
48519  pDelay->config.decay = value;
48520 }
48521 
48522 MA_API float ma_delay_get_decay(const ma_delay* pDelay)
48523 {
48524  if (pDelay == NULL) {
48525  return 0;
48526  }
48527 
48528  return pDelay->config.decay;
48529 }
48530 
48531 
48532 MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames)
48533 {
48534  ma_gainer_config config;
48535 
48536  MA_ZERO_OBJECT(&config);
48537  config.channels = channels;
48538  config.smoothTimeInFrames = smoothTimeInFrames;
48539 
48540  return config;
48541 }
48542 
48543 
48544 typedef struct
48545 {
48546  size_t sizeInBytes;
48547  size_t oldGainsOffset;
48548  size_t newGainsOffset;
48549 } ma_gainer_heap_layout;
48550 
48551 static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout)
48552 {
48553  MA_ASSERT(pHeapLayout != NULL);
48554 
48555  MA_ZERO_OBJECT(pHeapLayout);
48556 
48557  if (pConfig == NULL) {
48558  return MA_INVALID_ARGS;
48559  }
48560 
48561  if (pConfig->channels == 0) {
48562  return MA_INVALID_ARGS;
48563  }
48564 
48565  pHeapLayout->sizeInBytes = 0;
48566 
48567  /* Old gains. */
48568  pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes;
48569  pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
48570 
48571  /* New gains. */
48572  pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes;
48573  pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
48574 
48575  /* Alignment. */
48576  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
48577 
48578  return MA_SUCCESS;
48579 }
48580 
48581 
48582 MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes)
48583 {
48584  ma_result result;
48585  ma_gainer_heap_layout heapLayout;
48586 
48587  if (pHeapSizeInBytes == NULL) {
48588  return MA_INVALID_ARGS;
48589  }
48590 
48591  *pHeapSizeInBytes = 0;
48592 
48593  result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
48594  if (result != MA_SUCCESS) {
48595  return MA_INVALID_ARGS;
48596  }
48597 
48598  *pHeapSizeInBytes = heapLayout.sizeInBytes;
48599 
48600  return MA_SUCCESS;
48601 }
48602 
48603 
48604 MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer)
48605 {
48606  ma_result result;
48607  ma_gainer_heap_layout heapLayout;
48608  ma_uint32 iChannel;
48609 
48610  if (pGainer == NULL) {
48611  return MA_INVALID_ARGS;
48612  }
48613 
48614  MA_ZERO_OBJECT(pGainer);
48615 
48616  if (pConfig == NULL || pHeap == NULL) {
48617  return MA_INVALID_ARGS;
48618  }
48619 
48620  result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
48621  if (result != MA_SUCCESS) {
48622  return result;
48623  }
48624 
48625  pGainer->_pHeap = pHeap;
48626  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
48627 
48628  pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset);
48629  pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset);
48630  pGainer->masterVolume = 1;
48631 
48632  pGainer->config = *pConfig;
48633  pGainer->t = (ma_uint32)-1; /* No interpolation by default. */
48634 
48635  for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
48636  pGainer->pOldGains[iChannel] = 1;
48637  pGainer->pNewGains[iChannel] = 1;
48638  }
48639 
48640  return MA_SUCCESS;
48641 }
48642 
48643 MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer)
48644 {
48645  ma_result result;
48646  size_t heapSizeInBytes;
48647  void* pHeap;
48648 
48649  result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes);
48650  if (result != MA_SUCCESS) {
48651  return result; /* Failed to retrieve the size of the heap allocation. */
48652  }
48653 
48654  if (heapSizeInBytes > 0) {
48655  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48656  if (pHeap == NULL) {
48657  return MA_OUT_OF_MEMORY;
48658  }
48659  } else {
48660  pHeap = NULL;
48661  }
48662 
48663  result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer);
48664  if (result != MA_SUCCESS) {
48665  ma_free(pHeap, pAllocationCallbacks);
48666  return result;
48667  }
48668 
48669  pGainer->_ownsHeap = MA_TRUE;
48670  return MA_SUCCESS;
48671 }
48672 
48673 MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks)
48674 {
48675  if (pGainer == NULL) {
48676  return;
48677  }
48678 
48679  if (pGainer->_ownsHeap) {
48680  ma_free(pGainer->_pHeap, pAllocationCallbacks);
48681  }
48682 }
48683 
48684 static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel)
48685 {
48686  float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
48687  return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a);
48688 }
48689 
48690 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)
48691 {
48692  ma_uint64 iFrame;
48693  ma_uint32 iChannel;
48694  ma_uint64 interpolatedFrameCount;
48695 
48696  MA_ASSERT(pGainer != NULL);
48697 
48698  /*
48699  We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When
48700  linear interpolation is not needed we can do a simple volume adjustment which will be more
48701  efficient than a lerp with an alpha value of 1.
48702 
48703  To do this, all we need to do is determine how many frames need to have a lerp applied. Then we
48704  just process that number of frames with linear interpolation. After that we run on an optimized
48705  path which just applies the new gains without a lerp.
48706  */
48707  if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
48708  interpolatedFrameCount = 0;
48709  } else {
48710  interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames;
48711  if (interpolatedFrameCount > frameCount) {
48712  interpolatedFrameCount = frameCount;
48713  }
48714  }
48715 
48716  /*
48717  Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers
48718  so that the fast path can work naturally without consideration of the interpolated path.
48719  */
48720  if (interpolatedFrameCount > 0) {
48721  /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
48722  if (pFramesOut != NULL && pFramesIn != NULL) {
48723  /*
48724  All we're really doing here is moving the old gains towards the new gains. We don't want to
48725  be modifying the gains inside the ma_gainer object because that will break things. Instead
48726  we can make a copy here on the stack. For extreme channel counts we can fall back to a slower
48727  implementation which just uses a standard lerp.
48728  */
48729  float* pFramesOutF32 = (float*)pFramesOut;
48730  const float* pFramesInF32 = (const float*)pFramesIn;
48731  float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
48732  float d = 1.0f / pGainer->config.smoothTimeInFrames;
48733 
48734  if (pGainer->config.channels <= 32) {
48735  float pRunningGain[32];
48736  float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */
48737 
48738  /* Initialize the running gain. */
48739  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48740  float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume;
48741  pRunningGainDelta[iChannel] = t * d;
48742  pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a);
48743  }
48744 
48745  iFrame = 0;
48746 
48747  /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */
48748  if (pGainer->config.channels == 2) {
48749  #if defined(MA_SUPPORT_SSE2)
48750  if (ma_has_sse2()) {
48751  ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
48752 
48753  /* Expand some arrays so we can have a clean SIMD loop below. */
48754  __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]);
48755  __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]);
48756 
48757  for (; iFrame < unrolledLoopCount; iFrame += 1) {
48758  _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0));
48759  runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
48760  }
48761 
48762  iFrame = unrolledLoopCount << 1;
48763  } else
48764  #endif
48765  {
48766  /*
48767  Two different scalar implementations here. Clang (and I assume GCC) will vectorize
48768  both of these, but the bottom version results in a nicer vectorization with less
48769  instructions emitted. The problem, however, is that the bottom version runs slower
48770  when compiled with MSVC. The top version will be partially vectorized by MSVC.
48771  */
48772  #if defined(_MSC_VER) && !defined(__clang__)
48773  ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
48774 
48775  /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */
48776  pRunningGainDelta[2] = pRunningGainDelta[0];
48777  pRunningGainDelta[3] = pRunningGainDelta[1];
48778  pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0];
48779  pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1];
48780 
48781  for (; iFrame < unrolledLoopCount; iFrame += 1) {
48782  pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0];
48783  pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1];
48784  pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2];
48785  pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3];
48786 
48787  /* Move the running gain forward towards the new gain. */
48788  pRunningGain[0] += pRunningGainDelta[0];
48789  pRunningGain[1] += pRunningGainDelta[1];
48790  pRunningGain[2] += pRunningGainDelta[2];
48791  pRunningGain[3] += pRunningGainDelta[3];
48792  }
48793 
48794  iFrame = unrolledLoopCount << 1;
48795  #else
48796  for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48797  for (iChannel = 0; iChannel < 2; iChannel += 1) {
48798  pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel];
48799  }
48800 
48801  for (iChannel = 0; iChannel < 2; iChannel += 1) {
48802  pRunningGain[iChannel] += pRunningGainDelta[iChannel];
48803  }
48804  }
48805  #endif
48806  }
48807  } else if (pGainer->config.channels == 6) {
48808  #if defined(MA_SUPPORT_SSE2)
48809  if (ma_has_sse2()) {
48810  /*
48811  For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames
48812  at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays
48813  so we can do clean 4x SIMD operations.
48814  */
48815  ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
48816 
48817  /* Expand some arrays so we can have a clean SIMD loop below. */
48818  __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]);
48819  __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]);
48820  __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]);
48821 
48822  __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]);
48823  __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]);
48824  __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]);
48825 
48826  for (; iFrame < unrolledLoopCount; iFrame += 1) {
48827  _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0));
48828  _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1));
48829  _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2));
48830 
48831  runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
48832  runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);
48833  runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2);
48834  }
48835 
48836  iFrame = unrolledLoopCount << 1;
48837  } else
48838  #endif
48839  {
48840  for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48841  for (iChannel = 0; iChannel < 6; iChannel += 1) {
48842  pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel];
48843  }
48844 
48845  /* Move the running gain forward towards the new gain. */
48846  for (iChannel = 0; iChannel < 6; iChannel += 1) {
48847  pRunningGain[iChannel] += pRunningGainDelta[iChannel];
48848  }
48849  }
48850  }
48851  } else if (pGainer->config.channels == 8) {
48852  /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */
48853  #if defined(MA_SUPPORT_SSE2)
48854  if (ma_has_sse2()) {
48855  __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]);
48856  __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]);
48857  __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]);
48858  __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]);
48859 
48860  for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48861  _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0));
48862  _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1));
48863 
48864  runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
48865  runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);
48866  }
48867  } else
48868  #endif
48869  {
48870  /* This is crafted so that it auto-vectorizes when compiled with Clang. */
48871  for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48872  for (iChannel = 0; iChannel < 8; iChannel += 1) {
48873  pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel];
48874  }
48875 
48876  /* Move the running gain forward towards the new gain. */
48877  for (iChannel = 0; iChannel < 8; iChannel += 1) {
48878  pRunningGain[iChannel] += pRunningGainDelta[iChannel];
48879  }
48880  }
48881  }
48882  }
48883 
48884  for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48885  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48886  pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel];
48887  pRunningGain[iChannel] += pRunningGainDelta[iChannel];
48888  }
48889  }
48890  } else {
48891  /* 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. */
48892  for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) {
48893  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48894  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;
48895  }
48896 
48897  a += d;
48898  }
48899  }
48900  }
48901 
48902  /* Make sure the timer is updated. */
48903  pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames);
48904 
48905  /* Adjust our arguments so the next part can work normally. */
48906  frameCount -= interpolatedFrameCount;
48907  pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float));
48908  pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float));
48909  }
48910 
48911  /* All we need to do here is apply the new gains using an optimized path. */
48912  if (pFramesOut != NULL && pFramesIn != NULL) {
48913  if (pGainer->config.channels <= 32) {
48914  float gains[32];
48915  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48916  gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume;
48917  }
48918 
48919  ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains);
48920  } else {
48921  /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */
48922  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48923  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48924  ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume;
48925  }
48926  }
48927  }
48928  }
48929 
48930  /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
48931  if (pGainer->t == (ma_uint32)-1) {
48932  pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount);
48933  }
48934 
48935 #if 0
48936  if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
48937  /* Fast path. No gain calculation required. */
48938  ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);
48939  ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume);
48940 
48941  /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
48942  if (pGainer->t == (ma_uint32)-1) {
48943  pGainer->t = pGainer->config.smoothTimeInFrames;
48944  }
48945  } else {
48946  /* Slow path. Need to interpolate the gain for each channel individually. */
48947 
48948  /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
48949  if (pFramesOut != NULL && pFramesIn != NULL) {
48950  float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
48951  float d = 1.0f / pGainer->config.smoothTimeInFrames;
48952  ma_uint32 channelCount = pGainer->config.channels;
48953 
48954  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48955  for (iChannel = 0; iChannel < channelCount; iChannel += 1) {
48956  pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume;
48957  }
48958 
48959  pFramesOutF32 += channelCount;
48960  pFramesInF32 += channelCount;
48961 
48962  a += d;
48963  if (a > 1) {
48964  a = 1;
48965  }
48966  }
48967  }
48968 
48969  pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);
48970 
48971  #if 0 /* Reference implementation. */
48972  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48973  /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
48974  if (pFramesOut != NULL && pFramesIn != NULL) {
48975  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48976  pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume;
48977  }
48978  }
48979 
48980  /* Move interpolation time forward, but don't go beyond our smoothing time. */
48981  pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);
48982  }
48983  #endif
48984  }
48985 #endif
48986 
48987  return MA_SUCCESS;
48988 }
48989 
48990 MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48991 {
48992  if (pGainer == NULL) {
48993  return MA_INVALID_ARGS;
48994  }
48995 
48996  /*
48997  ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which
48998  helps with auto-vectorization.
48999  */
49000  return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount);
49001 }
49002 
49003 static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel)
49004 {
49005  pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel);
49006  pGainer->pNewGains[iChannel] = newGain;
49007 }
49008 
49009 static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer)
49010 {
49011  if (pGainer->t == (ma_uint32)-1) {
49012  pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */
49013  } else {
49014  pGainer->t = 0;
49015  }
49016 }
49017 
49018 MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain)
49019 {
49020  ma_uint32 iChannel;
49021 
49022  if (pGainer == NULL) {
49023  return MA_INVALID_ARGS;
49024  }
49025 
49026  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
49027  ma_gainer_set_gain_by_index(pGainer, newGain, iChannel);
49028  }
49029 
49030  /* 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. */
49031  ma_gainer_reset_smoothing_time(pGainer);
49032 
49033  return MA_SUCCESS;
49034 }
49035 
49036 MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains)
49037 {
49038  ma_uint32 iChannel;
49039 
49040  if (pGainer == NULL || pNewGains == NULL) {
49041  return MA_INVALID_ARGS;
49042  }
49043 
49044  for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
49045  ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel);
49046  }
49047 
49048  /* 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. */
49049  ma_gainer_reset_smoothing_time(pGainer);
49050 
49051  return MA_SUCCESS;
49052 }
49053 
49054 MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume)
49055 {
49056  if (pGainer == NULL) {
49057  return MA_INVALID_ARGS;
49058  }
49059 
49060  pGainer->masterVolume = volume;
49061 
49062  return MA_SUCCESS;
49063 }
49064 
49065 MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume)
49066 {
49067  if (pGainer == NULL || pVolume == NULL) {
49068  return MA_INVALID_ARGS;
49069  }
49070 
49071  *pVolume = pGainer->masterVolume;
49072 
49073  return MA_SUCCESS;
49074 }
49075 
49076 
49077 MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels)
49078 {
49079  ma_panner_config config;
49080 
49081  MA_ZERO_OBJECT(&config);
49082  config.format = format;
49083  config.channels = channels;
49084  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. */
49085  config.pan = 0;
49086 
49087  return config;
49088 }
49089 
49090 
49091 MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner)
49092 {
49093  if (pPanner == NULL) {
49094  return MA_INVALID_ARGS;
49095  }
49096 
49097  MA_ZERO_OBJECT(pPanner);
49098 
49099  if (pConfig == NULL) {
49100  return MA_INVALID_ARGS;
49101  }
49102 
49103  pPanner->format = pConfig->format;
49104  pPanner->channels = pConfig->channels;
49105  pPanner->mode = pConfig->mode;
49106  pPanner->pan = pConfig->pan;
49107 
49108  return MA_SUCCESS;
49109 }
49110 
49111 static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
49112 {
49113  ma_uint64 iFrame;
49114 
49115  if (pan > 0) {
49116  float factor = 1.0f - pan;
49117  if (pFramesOut == pFramesIn) {
49118  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49119  pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
49120  }
49121  } else {
49122  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49123  pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
49124  pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1];
49125  }
49126  }
49127  } else {
49128  float factor = 1.0f + pan;
49129  if (pFramesOut == pFramesIn) {
49130  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49131  pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
49132  }
49133  } else {
49134  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49135  pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0];
49136  pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
49137  }
49138  }
49139  }
49140 }
49141 
49142 static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
49143 {
49144  if (pan == 0) {
49145  /* Fast path. No panning required. */
49146  if (pFramesOut == pFramesIn) {
49147  /* No-op */
49148  } else {
49149  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
49150  }
49151 
49152  return;
49153  }
49154 
49155  switch (format) {
49156  case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
49157 
49158  /* Unknown format. Just copy. */
49159  default:
49160  {
49161  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
49162  } break;
49163  }
49164 }
49165 
49166 
49167 static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
49168 {
49169  ma_uint64 iFrame;
49170 
49171  if (pan > 0) {
49172  float factorL0 = 1.0f - pan;
49173  float factorL1 = 0.0f + pan;
49174 
49175  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49176  float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0);
49177  float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1];
49178 
49179  pFramesOut[iFrame*2 + 0] = sample0;
49180  pFramesOut[iFrame*2 + 1] = sample1;
49181  }
49182  } else {
49183  float factorR0 = 0.0f - pan;
49184  float factorR1 = 1.0f + pan;
49185 
49186  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49187  float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0);
49188  float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1);
49189 
49190  pFramesOut[iFrame*2 + 0] = sample0;
49191  pFramesOut[iFrame*2 + 1] = sample1;
49192  }
49193  }
49194 }
49195 
49196 static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
49197 {
49198  if (pan == 0) {
49199  /* Fast path. No panning required. */
49200  if (pFramesOut == pFramesIn) {
49201  /* No-op */
49202  } else {
49203  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
49204  }
49205 
49206  return;
49207  }
49208 
49209  switch (format) {
49210  case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
49211 
49212  /* Unknown format. Just copy. */
49213  default:
49214  {
49215  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
49216  } break;
49217  }
49218 }
49219 
49220 MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49221 {
49222  if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) {
49223  return MA_INVALID_ARGS;
49224  }
49225 
49226  if (pPanner->channels == 2) {
49227  /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */
49228  if (pPanner->mode == ma_pan_mode_balance) {
49229  ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
49230  } else {
49231  ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
49232  }
49233  } else {
49234  if (pPanner->channels == 1) {
49235  /* Panning has no effect on mono streams. */
49236  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
49237  } else {
49238  /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */
49239  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
49240  }
49241  }
49242 
49243  return MA_SUCCESS;
49244 }
49245 
49246 MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode)
49247 {
49248  if (pPanner == NULL) {
49249  return;
49250  }
49251 
49252  pPanner->mode = mode;
49253 }
49254 
49255 MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner)
49256 {
49257  if (pPanner == NULL) {
49258  return ma_pan_mode_balance;
49259  }
49260 
49261  return pPanner->mode;
49262 }
49263 
49264 MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan)
49265 {
49266  if (pPanner == NULL) {
49267  return;
49268  }
49269 
49270  pPanner->pan = ma_clamp(pan, -1.0f, 1.0f);
49271 }
49272 
49273 MA_API float ma_panner_get_pan(const ma_panner* pPanner)
49274 {
49275  if (pPanner == NULL) {
49276  return 0;
49277  }
49278 
49279  return pPanner->pan;
49280 }
49281 
49282 
49283 
49284 
49285 MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
49286 {
49287  ma_fader_config config;
49288 
49289  MA_ZERO_OBJECT(&config);
49290  config.format = format;
49291  config.channels = channels;
49292  config.sampleRate = sampleRate;
49293 
49294  return config;
49295 }
49296 
49297 
49298 MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader)
49299 {
49300  if (pFader == NULL) {
49301  return MA_INVALID_ARGS;
49302  }
49303 
49304  MA_ZERO_OBJECT(pFader);
49305 
49306  if (pConfig == NULL) {
49307  return MA_INVALID_ARGS;
49308  }
49309 
49310  /* Only f32 is supported for now. */
49311  if (pConfig->format != ma_format_f32) {
49312  return MA_INVALID_ARGS;
49313  }
49314 
49315  pFader->config = *pConfig;
49316  pFader->volumeBeg = 1;
49317  pFader->volumeEnd = 1;
49318  pFader->lengthInFrames = 0;
49319  pFader->cursorInFrames = 0;
49320 
49321  return MA_SUCCESS;
49322 }
49323 
49324 MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49325 {
49326  if (pFader == NULL) {
49327  return MA_INVALID_ARGS;
49328  }
49329 
49330  /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */
49331  if (pFader->cursorInFrames < 0) {
49332  ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames;
49333  if (absCursorInFrames > frameCount) {
49334  absCursorInFrames = frameCount;
49335  }
49336 
49337  ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels);
49338 
49339  pFader->cursorInFrames += absCursorInFrames;
49340  frameCount -= absCursorInFrames;
49341  pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames);
49342  pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames);
49343  }
49344 
49345  if (pFader->cursorInFrames >= 0) {
49346  /*
49347  For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for
49348  the conversion to a float which we use for the linear interpolation. This might be changed later.
49349  */
49350  if (frameCount + pFader->cursorInFrames > UINT_MAX) {
49351  frameCount = UINT_MAX - pFader->cursorInFrames;
49352  }
49353 
49354  /* Optimized path if volumeBeg and volumeEnd are equal. */
49355  if (pFader->volumeBeg == pFader->volumeEnd) {
49356  if (pFader->volumeBeg == 1) {
49357  /* Straight copy. */
49358  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels);
49359  } else {
49360  /* Copy with volume. */
49361  ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg);
49362  }
49363  } else {
49364  /* Slower path. Volumes are different, so may need to do an interpolation. */
49365  if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) {
49366  /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */
49367  ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
49368  } else {
49369  /* Slow path. This is where we do the actual fading. */
49370  ma_uint64 iFrame;
49371  ma_uint32 iChannel;
49372 
49373  /* For now we only support f32. Support for other formats might be added later. */
49374  if (pFader->config.format == ma_format_f32) {
49375  const float* pFramesInF32 = (const float*)pFramesIn;
49376  /* */ float* pFramesOutF32 = ( float*)pFramesOut;
49377 
49378  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49379  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. */
49380  float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a);
49381 
49382  for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) {
49383  pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume;
49384  }
49385  }
49386  } else {
49387  return MA_NOT_IMPLEMENTED;
49388  }
49389  }
49390  }
49391  }
49392 
49393  pFader->cursorInFrames += frameCount;
49394 
49395  return MA_SUCCESS;
49396 }
49397 
49398 MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
49399 {
49400  if (pFader == NULL) {
49401  return;
49402  }
49403 
49404  if (pFormat != NULL) {
49405  *pFormat = pFader->config.format;
49406  }
49407 
49408  if (pChannels != NULL) {
49409  *pChannels = pFader->config.channels;
49410  }
49411 
49412  if (pSampleRate != NULL) {
49413  *pSampleRate = pFader->config.sampleRate;
49414  }
49415 }
49416 
49417 MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
49418 {
49419  ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0);
49420 }
49421 
49422 MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames)
49423 {
49424  if (pFader == NULL) {
49425  return;
49426  }
49427 
49428  /* If the volume is negative, use current volume. */
49429  if (volumeBeg < 0) {
49430  volumeBeg = ma_fader_get_current_volume(pFader);
49431  }
49432 
49433  /*
49434  The length needs to be clamped to 32-bits due to how we convert it to a float for linear
49435  interpolation reasons. I might change this requirement later, but for now it's not important.
49436  */
49437  if (lengthInFrames > UINT_MAX) {
49438  lengthInFrames = UINT_MAX;
49439  }
49440 
49441  /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */
49442  if (startOffsetInFrames > INT_MAX) {
49443  startOffsetInFrames = INT_MAX;
49444  }
49445 
49446  pFader->volumeBeg = volumeBeg;
49447  pFader->volumeEnd = volumeEnd;
49448  pFader->lengthInFrames = lengthInFrames;
49449  pFader->cursorInFrames = -startOffsetInFrames;
49450 }
49451 
49452 MA_API float ma_fader_get_current_volume(const ma_fader* pFader)
49453 {
49454  if (pFader == NULL) {
49455  return 0.0f;
49456  }
49457 
49458  /* Any frames prior to the start of the fade period will be at unfaded volume. */
49459  if (pFader->cursorInFrames < 0) {
49460  return 1.0f;
49461  }
49462 
49463  /* The current volume depends on the position of the cursor. */
49464  if (pFader->cursorInFrames == 0) {
49465  return pFader->volumeBeg;
49466  } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */
49467  return pFader->volumeEnd;
49468  } else {
49469  /* 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. */
49470  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(). */
49471  }
49472 }
49473 
49474 
49475 
49476 
49477 
49478 MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z)
49479 {
49480  ma_vec3f v;
49481 
49482  v.x = x;
49483  v.y = y;
49484  v.z = z;
49485 
49486  return v;
49487 }
49488 
49489 MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b)
49490 {
49491  return ma_vec3f_init_3f(
49492  a.x - b.x,
49493  a.y - b.y,
49494  a.z - b.z
49495  );
49496 }
49497 
49498 MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a)
49499 {
49500  return ma_vec3f_init_3f(
49501  -a.x,
49502  -a.y,
49503  -a.z
49504  );
49505 }
49506 
49507 MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b)
49508 {
49509  return a.x*b.x + a.y*b.y + a.z*b.z;
49510 }
49511 
49512 MA_API float ma_vec3f_len2(ma_vec3f v)
49513 {
49514  return ma_vec3f_dot(v, v);
49515 }
49516 
49517 MA_API float ma_vec3f_len(ma_vec3f v)
49518 {
49519  return (float)ma_sqrtd(ma_vec3f_len2(v));
49520 }
49521 
49522 
49523 
49524 MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b)
49525 {
49526  return ma_vec3f_len(ma_vec3f_sub(a, b));
49527 }
49528 
49529 MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v)
49530 {
49531  float invLen;
49532  float len2 = ma_vec3f_len2(v);
49533  if (len2 == 0) {
49534  return ma_vec3f_init_3f(0, 0, 0);
49535  }
49536 
49537  invLen = ma_rsqrtf(len2);
49538  v.x *= invLen;
49539  v.y *= invLen;
49540  v.z *= invLen;
49541 
49542  return v;
49543 }
49544 
49545 MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b)
49546 {
49547  return ma_vec3f_init_3f(
49548  a.y*b.z - a.z*b.y,
49549  a.z*b.x - a.x*b.z,
49550  a.x*b.y - a.y*b.x
49551  );
49552 }
49553 
49554 
49555 MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value)
49556 {
49557  v->v = value;
49558  v->lock = 0; /* Important this is initialized to 0. */
49559 }
49560 
49561 MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value)
49562 {
49563  ma_spinlock_lock(&v->lock);
49564  {
49565  v->v = value;
49566  }
49567  ma_spinlock_unlock(&v->lock);
49568 }
49569 
49570 MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v)
49571 {
49572  ma_vec3f r;
49573 
49574  ma_spinlock_lock(&v->lock);
49575  {
49576  r = v->v;
49577  }
49578  ma_spinlock_unlock(&v->lock);
49579 
49580  return r;
49581 }
49582 
49583 
49584 
49585 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);
49586 static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition);
49587 
49588 
49589 #ifndef MA_DEFAULT_SPEED_OF_SOUND
49590 #define MA_DEFAULT_SPEED_OF_SOUND 343.3f
49591 #endif
49592 
49593 /*
49594 These vectors represent the direction that speakers are facing from the center point. They're used
49595 for panning in the spatializer. Must be normalized.
49596 */
49597 static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = {
49598  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */
49599  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */
49600  {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */
49601  {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */
49602  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */
49603  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */
49604  {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */
49605  {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */
49606  {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */
49607  {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
49608  { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */
49609  {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */
49610  {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */
49611  { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */
49612  {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */
49613  { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */
49614  {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */
49615  {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */
49616  { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */
49617  {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */
49618  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */
49619  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */
49620  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */
49621  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */
49622  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */
49623  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */
49624  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */
49625  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */
49626  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */
49627  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */
49628  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */
49629  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */
49630  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */
49631  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */
49632  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */
49633  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */
49634  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */
49635  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */
49636  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */
49637  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */
49638  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */
49639  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */
49640  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */
49641  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */
49642  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */
49643  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */
49644  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */
49645  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */
49646  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */
49647  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */
49648  { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */
49649  { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */
49650 };
49651 
49652 static ma_vec3f ma_get_channel_direction(ma_channel channel)
49653 {
49654  if (channel >= MA_CHANNEL_POSITION_COUNT) {
49655  return ma_vec3f_init_3f(0, 0, -1);
49656  } else {
49657  return g_maChannelDirections[channel];
49658  }
49659 }
49660 
49661 
49662 
49663 static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff)
49664 {
49665  if (minDistance >= maxDistance) {
49666  return 1; /* To avoid division by zero. Do not attenuate. */
49667  }
49668 
49669  return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance));
49670 }
49671 
49672 static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff)
49673 {
49674  if (minDistance >= maxDistance) {
49675  return 1; /* To avoid division by zero. Do not attenuate. */
49676  }
49677 
49678  return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance);
49679 }
49680 
49681 static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff)
49682 {
49683  if (minDistance >= maxDistance) {
49684  return 1; /* To avoid division by zero. Do not attenuate. */
49685  }
49686 
49687  return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff);
49688 }
49689 
49690 
49691 /*
49692 Dopper Effect calculation taken from the OpenAL spec, with two main differences:
49693 
49694  1) The source to listener vector will have already been calcualted at an earlier step so we can
49695  just use that directly. We need only the position of the source relative to the origin.
49696 
49697  2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight
49698  into the resampler directly.
49699 */
49700 static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor)
49701 {
49702  float len;
49703  float vls;
49704  float vss;
49705 
49706  len = ma_vec3f_len(relativePosition);
49707 
49708  /*
49709  There's a case where the position of the source will be right on top of the listener in which
49710  case the length will be 0 and we'll end up with a division by zero. We can just return a ratio
49711  of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary.
49712  */
49713  if (len == 0) {
49714  return 1.0;
49715  }
49716 
49717  vls = ma_vec3f_dot(relativePosition, listenVelocity) / len;
49718  vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len;
49719 
49720  vls = ma_min(vls, speedOfSound / dopplerFactor);
49721  vss = ma_min(vss, speedOfSound / dopplerFactor);
49722 
49723  return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss);
49724 }
49725 
49726 
49727 static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount)
49728 {
49729  /*
49730  Special case for stereo. Want to default the left and right speakers to side left and side
49731  right so that they're facing directly down the X axis rather than slightly forward. Not
49732  doing this will result in sounds being quieter when behind the listener. This might
49733  actually be good for some scenerios, but I don't think it's an appropriate default because
49734  it can be a bit unexpected.
49735  */
49736  if (channelCount == 2) {
49737  pChannelMap[0] = MA_CHANNEL_SIDE_LEFT;
49738  pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT;
49739  } else {
49740  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
49741  }
49742 }
49743 
49744 
49745 MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut)
49746 {
49748 
49749  MA_ZERO_OBJECT(&config);
49750  config.channelsOut = channelsOut;
49751  config.pChannelMapOut = NULL;
49752  config.handedness = ma_handedness_right;
49753  config.worldUp = ma_vec3f_init_3f(0, 1, 0);
49754  config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
49755  config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */
49756  config.coneOuterGain = 0;
49757  config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */
49758 
49759  return config;
49760 }
49761 
49762 
49763 typedef struct
49764 {
49765  size_t sizeInBytes;
49766  size_t channelMapOutOffset;
49767 } ma_spatializer_listener_heap_layout;
49768 
49769 static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout)
49770 {
49771  MA_ASSERT(pHeapLayout != NULL);
49772 
49773  MA_ZERO_OBJECT(pHeapLayout);
49774 
49775  if (pConfig == NULL) {
49776  return MA_INVALID_ARGS;
49777  }
49778 
49779  if (pConfig->channelsOut == 0) {
49780  return MA_INVALID_ARGS;
49781  }
49782 
49783  pHeapLayout->sizeInBytes = 0;
49784 
49785  /* Channel map. We always need this, even for passthroughs. */
49786  pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
49787  pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut);
49788 
49789  return MA_SUCCESS;
49790 }
49791 
49792 
49793 MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes)
49794 {
49795  ma_result result;
49796  ma_spatializer_listener_heap_layout heapLayout;
49797 
49798  if (pHeapSizeInBytes == NULL) {
49799  return MA_INVALID_ARGS;
49800  }
49801 
49802  *pHeapSizeInBytes = 0;
49803 
49804  result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
49805  if (result != MA_SUCCESS) {
49806  return result;
49807  }
49808 
49809  *pHeapSizeInBytes = heapLayout.sizeInBytes;
49810 
49811  return MA_SUCCESS;
49812 }
49813 
49814 MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener)
49815 {
49816  ma_result result;
49817  ma_spatializer_listener_heap_layout heapLayout;
49818 
49819  if (pListener == NULL) {
49820  return MA_INVALID_ARGS;
49821  }
49822 
49823  MA_ZERO_OBJECT(pListener);
49824 
49825  result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
49826  if (result != MA_SUCCESS) {
49827  return result;
49828  }
49829 
49830  pListener->_pHeap = pHeap;
49831  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
49832 
49833  pListener->config = *pConfig;
49834  ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0));
49835  ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1));
49836  ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0));
49837  pListener->isEnabled = MA_TRUE;
49838 
49839  /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
49840  if (pListener->config.handedness == ma_handedness_left) {
49841  ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener));
49842  ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z);
49843  }
49844 
49845 
49846  /* We must always have a valid channel map. */
49847  pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
49848 
49849  /* Use a slightly different default channel map for stereo. */
49850  if (pConfig->pChannelMapOut == NULL) {
49851  ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut);
49852  } else {
49853  ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
49854  }
49855 
49856  return MA_SUCCESS;
49857 }
49858 
49859 MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener)
49860 {
49861  ma_result result;
49862  size_t heapSizeInBytes;
49863  void* pHeap;
49864 
49865  result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes);
49866  if (result != MA_SUCCESS) {
49867  return result;
49868  }
49869 
49870  if (heapSizeInBytes > 0) {
49871  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49872  if (pHeap == NULL) {
49873  return MA_OUT_OF_MEMORY;
49874  }
49875  } else {
49876  pHeap = NULL;
49877  }
49878 
49879  result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener);
49880  if (result != MA_SUCCESS) {
49881  ma_free(pHeap, pAllocationCallbacks);
49882  return result;
49883  }
49884 
49885  pListener->_ownsHeap = MA_TRUE;
49886  return MA_SUCCESS;
49887 }
49888 
49889 MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks)
49890 {
49891  if (pListener == NULL) {
49892  return;
49893  }
49894 
49895  if (pListener->_ownsHeap) {
49896  ma_free(pListener->_pHeap, pAllocationCallbacks);
49897  }
49898 }
49899 
49900 MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener)
49901 {
49902  if (pListener == NULL) {
49903  return NULL;
49904  }
49905 
49906  return pListener->config.pChannelMapOut;
49907 }
49908 
49909 MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
49910 {
49911  if (pListener == NULL) {
49912  return;
49913  }
49914 
49915  pListener->config.coneInnerAngleInRadians = innerAngleInRadians;
49916  pListener->config.coneOuterAngleInRadians = outerAngleInRadians;
49917  pListener->config.coneOuterGain = outerGain;
49918 }
49919 
49920 MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
49921 {
49922  if (pListener == NULL) {
49923  return;
49924  }
49925 
49926  if (pInnerAngleInRadians != NULL) {
49927  *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians;
49928  }
49929 
49930  if (pOuterAngleInRadians != NULL) {
49931  *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians;
49932  }
49933 
49934  if (pOuterGain != NULL) {
49935  *pOuterGain = pListener->config.coneOuterGain;
49936  }
49937 }
49938 
49939 MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z)
49940 {
49941  if (pListener == NULL) {
49942  return;
49943  }
49944 
49945  ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z));
49946 }
49947 
49948 MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener)
49949 {
49950  if (pListener == NULL) {
49951  return ma_vec3f_init_3f(0, 0, 0);
49952  }
49953 
49954  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. */
49955 }
49956 
49957 MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z)
49958 {
49959  if (pListener == NULL) {
49960  return;
49961  }
49962 
49963  ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z));
49964 }
49965 
49966 MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener)
49967 {
49968  if (pListener == NULL) {
49969  return ma_vec3f_init_3f(0, 0, -1);
49970  }
49971 
49972  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. */
49973 }
49974 
49975 MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z)
49976 {
49977  if (pListener == NULL) {
49978  return;
49979  }
49980 
49981  ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z));
49982 }
49983 
49984 MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener)
49985 {
49986  if (pListener == NULL) {
49987  return ma_vec3f_init_3f(0, 0, 0);
49988  }
49989 
49990  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. */
49991 }
49992 
49993 MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound)
49994 {
49995  if (pListener == NULL) {
49996  return;
49997  }
49998 
49999  pListener->config.speedOfSound = speedOfSound;
50000 }
50001 
50002 MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener)
50003 {
50004  if (pListener == NULL) {
50005  return 0;
50006  }
50007 
50008  return pListener->config.speedOfSound;
50009 }
50010 
50011 MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z)
50012 {
50013  if (pListener == NULL) {
50014  return;
50015  }
50016 
50017  pListener->config.worldUp = ma_vec3f_init_3f(x, y, z);
50018 }
50019 
50020 MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener)
50021 {
50022  if (pListener == NULL) {
50023  return ma_vec3f_init_3f(0, 1, 0);
50024  }
50025 
50026  return pListener->config.worldUp;
50027 }
50028 
50029 MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled)
50030 {
50031  if (pListener == NULL) {
50032  return;
50033  }
50034 
50035  pListener->isEnabled = isEnabled;
50036 }
50037 
50038 MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener)
50039 {
50040  if (pListener == NULL) {
50041  return MA_FALSE;
50042  }
50043 
50044  return pListener->isEnabled;
50045 }
50046 
50047 
50048 
50049 
50050 MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut)
50051 {
50052  ma_spatializer_config config;
50053 
50054  MA_ZERO_OBJECT(&config);
50055  config.channelsIn = channelsIn;
50056  config.channelsOut = channelsOut;
50057  config.pChannelMapIn = NULL;
50058  config.attenuationModel = ma_attenuation_model_inverse;
50059  config.positioning = ma_positioning_absolute;
50060  config.handedness = ma_handedness_right;
50061  config.minGain = 0;
50062  config.maxGain = 1;
50063  config.minDistance = 1;
50064  config.maxDistance = MA_FLT_MAX;
50065  config.rolloff = 1;
50066  config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
50067  config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */
50068  config.coneOuterGain = 0.0f;
50069  config.dopplerFactor = 1;
50070  config.directionalAttenuationFactor = 1;
50071  config.minSpatializationChannelGain = 0.2f;
50072  config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */
50073 
50074  return config;
50075 }
50076 
50077 
50078 static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig)
50079 {
50080  MA_ASSERT(pConfig != NULL);
50081  return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames);
50082 }
50083 
50084 static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig)
50085 {
50086  MA_ASSERT(pConfig != NULL);
50087 
50088  if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
50089  return MA_INVALID_ARGS;
50090  }
50091 
50092  return MA_SUCCESS;
50093 }
50094 
50095 typedef struct
50096 {
50097  size_t sizeInBytes;
50098  size_t channelMapInOffset;
50099  size_t newChannelGainsOffset;
50100  size_t gainerOffset;
50101 } ma_spatializer_heap_layout;
50102 
50103 static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout)
50104 {
50105  ma_result result;
50106 
50107  MA_ASSERT(pHeapLayout != NULL);
50108 
50109  MA_ZERO_OBJECT(pHeapLayout);
50110 
50111  if (pConfig == NULL) {
50112  return MA_INVALID_ARGS;
50113  }
50114 
50115  result = ma_spatializer_validate_config(pConfig);
50116  if (result != MA_SUCCESS) {
50117  return result;
50118  }
50119 
50120  pHeapLayout->sizeInBytes = 0;
50121 
50122  /* Channel map. */
50123  pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */
50124  if (pConfig->pChannelMapIn != NULL) {
50125  pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
50126  pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn);
50127  }
50128 
50129  /* New channel gains for output. */
50130  pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes;
50131  pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut);
50132 
50133  /* Gainer. */
50134  {
50135  size_t gainerHeapSizeInBytes;
50136  ma_gainer_config gainerConfig;
50137 
50138  gainerConfig = ma_spatializer_gainer_config_init(pConfig);
50139 
50140  result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes);
50141  if (result != MA_SUCCESS) {
50142  return result;
50143  }
50144 
50145  pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
50146  pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes);
50147  }
50148 
50149  return MA_SUCCESS;
50150 }
50151 
50152 MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes)
50153 {
50154  ma_result result;
50155  ma_spatializer_heap_layout heapLayout;
50156 
50157  if (pHeapSizeInBytes == NULL) {
50158  return MA_INVALID_ARGS;
50159  }
50160 
50161  *pHeapSizeInBytes = 0; /* Safety. */
50162 
50163  result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
50164  if (result != MA_SUCCESS) {
50165  return result;
50166  }
50167 
50168  *pHeapSizeInBytes = heapLayout.sizeInBytes;
50169 
50170  return MA_SUCCESS;
50171 }
50172 
50173 
50174 MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer)
50175 {
50176  ma_result result;
50177  ma_spatializer_heap_layout heapLayout;
50178  ma_gainer_config gainerConfig;
50179 
50180  if (pSpatializer == NULL) {
50181  return MA_INVALID_ARGS;
50182  }
50183 
50184  MA_ZERO_OBJECT(pSpatializer);
50185 
50186  if (pConfig == NULL || pHeap == NULL) {
50187  return MA_INVALID_ARGS;
50188  }
50189 
50190  result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
50191  if (result != MA_SUCCESS) {
50192  return result;
50193  }
50194 
50195  pSpatializer->_pHeap = pHeap;
50196  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
50197 
50198  pSpatializer->channelsIn = pConfig->channelsIn;
50199  pSpatializer->channelsOut = pConfig->channelsOut;
50200  pSpatializer->attenuationModel = pConfig->attenuationModel;
50201  pSpatializer->positioning = pConfig->positioning;
50202  pSpatializer->handedness = pConfig->handedness;
50203  pSpatializer->minGain = pConfig->minGain;
50204  pSpatializer->maxGain = pConfig->maxGain;
50205  pSpatializer->minDistance = pConfig->minDistance;
50206  pSpatializer->maxDistance = pConfig->maxDistance;
50207  pSpatializer->rolloff = pConfig->rolloff;
50208  pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians;
50209  pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians;
50210  pSpatializer->coneOuterGain = pConfig->coneOuterGain;
50211  pSpatializer->dopplerFactor = pConfig->dopplerFactor;
50212  pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain;
50213  pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor;
50214  pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames;
50215  ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0));
50216  ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1));
50217  ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0));
50218  pSpatializer->dopplerPitch = 1;
50219 
50220  /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
50221  if (pSpatializer->handedness == ma_handedness_left) {
50222  ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer));
50223  ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z);
50224  }
50225 
50226  /* Channel map. This will be on the heap. */
50227  if (pConfig->pChannelMapIn != NULL) {
50228  pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
50229  ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn);
50230  }
50231 
50232  /* New channel gains for output channels. */
50233  pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset);
50234 
50235  /* Gainer. */
50236  gainerConfig = ma_spatializer_gainer_config_init(pConfig);
50237 
50238  result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer);
50239  if (result != MA_SUCCESS) {
50240  return result; /* Failed to initialize the gainer. */
50241  }
50242 
50243  return MA_SUCCESS;
50244 }
50245 
50246 MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer)
50247 {
50248  ma_result result;
50249  size_t heapSizeInBytes;
50250  void* pHeap;
50251 
50252  /* We'll need a heap allocation to retrieve the size. */
50253  result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes);
50254  if (result != MA_SUCCESS) {
50255  return result;
50256  }
50257 
50258  if (heapSizeInBytes > 0) {
50259  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
50260  if (pHeap == NULL) {
50261  return MA_OUT_OF_MEMORY;
50262  }
50263  } else {
50264  pHeap = NULL;
50265  }
50266 
50267  result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer);
50268  if (result != MA_SUCCESS) {
50269  ma_free(pHeap, pAllocationCallbacks);
50270  return result;
50271  }
50272 
50273  pSpatializer->_ownsHeap = MA_TRUE;
50274  return MA_SUCCESS;
50275 }
50276 
50277 MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks)
50278 {
50279  if (pSpatializer == NULL) {
50280  return;
50281  }
50282 
50283  ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks);
50284 
50285  if (pSpatializer->_ownsHeap) {
50286  ma_free(pSpatializer->_pHeap, pAllocationCallbacks);
50287  }
50288 }
50289 
50290 static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain)
50291 {
50292  /*
50293  Angular attenuation.
50294 
50295  Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
50296  this out for ourselves at the expense of possibly being inconsistent with other implementations.
50297 
50298  To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
50299  just need to get the direction from the source to the listener and then do a dot product against that and the
50300  direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
50301  angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
50302  the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
50303  */
50304  if (coneInnerAngleInRadians < 6.283185f) {
50305  float angularGain = 1;
50306  float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f);
50307  float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f);
50308  float d;
50309 
50310  d = ma_vec3f_dot(dirA, dirB);
50311 
50312  if (d > cutoffInner) {
50313  /* It's inside the inner angle. */
50314  angularGain = 1;
50315  } else {
50316  /* It's outside the inner angle. */
50317  if (d > cutoffOuter) {
50318  /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */
50319  angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter));
50320  } else {
50321  /* It's outside the outer angle. */
50322  angularGain = coneOuterGain;
50323  }
50324  }
50325 
50326  /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/
50327  return angularGain;
50328  } else {
50329  /* Inner angle is 360 degrees so no need to do any attenuation. */
50330  return 1;
50331  }
50332 }
50333 
50334 MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
50335 {
50336  ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn;
50337  ma_channel* pChannelMapOut = pListener->config.pChannelMapOut;
50338 
50339  if (pSpatializer == NULL) {
50340  return MA_INVALID_ARGS;
50341  }
50342 
50343  /* If we're not spatializing we need to run an optimized path. */
50344  if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) {
50345  if (ma_spatializer_listener_is_enabled(pListener)) {
50346  /* No attenuation is required, but we'll need to do some channel conversion. */
50347  if (pSpatializer->channelsIn == pSpatializer->channelsOut) {
50348  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn);
50349  } else {
50350  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. */
50351  }
50352  } else {
50353  /* The listener is disabled. Output silence. */
50354  ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
50355  }
50356 
50357  /*
50358  We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is
50359  the correct thinking so might need to review this later.
50360  */
50361  pSpatializer->dopplerPitch = 1;
50362  } else {
50363  /*
50364  Let's first determine which listener the sound is closest to. Need to keep in mind that we
50365  might not have a world or any listeners, in which case we just spatializer based on the
50366  listener being positioned at the origin (0, 0, 0).
50367  */
50368  ma_vec3f relativePosNormalized;
50369  ma_vec3f relativePos; /* The position relative to the listener. */
50370  ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */
50371  ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */
50372  float speedOfSound;
50373  float distance = 0;
50374  float gain = 1;
50375  ma_uint32 iChannel;
50376  const ma_uint32 channelsOut = pSpatializer->channelsOut;
50377  const ma_uint32 channelsIn = pSpatializer->channelsIn;
50378  float minDistance = ma_spatializer_get_min_distance(pSpatializer);
50379  float maxDistance = ma_spatializer_get_max_distance(pSpatializer);
50380  float rolloff = ma_spatializer_get_rolloff(pSpatializer);
50381  float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer);
50382 
50383  /*
50384  We'll need the listener velocity for doppler pitch calculations. The speed of sound is
50385  defined by the listener, so we'll grab that here too.
50386  */
50387  if (pListener != NULL) {
50388  listenerVel = ma_spatializer_listener_get_velocity(pListener);
50389  speedOfSound = pListener->config.speedOfSound;
50390  } else {
50391  listenerVel = ma_vec3f_init_3f(0, 0, 0);
50392  speedOfSound = MA_DEFAULT_SPEED_OF_SOUND;
50393  }
50394 
50395  if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
50396  /* There's no listener or we're using relative positioning. */
50397  relativePos = ma_spatializer_get_position(pSpatializer);
50398  relativeDir = ma_spatializer_get_direction(pSpatializer);
50399  } else {
50400  /*
50401  We've found a listener and we're using absolute positioning. We need to transform the
50402  sound's position and direction so that it's relative to listener. Later on we'll use
50403  this for determining the factors to apply to each channel to apply the panning effect.
50404  */
50405  ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir);
50406  }
50407 
50408  distance = ma_vec3f_len(relativePos);
50409 
50410  /* We've gathered the data, so now we can apply some spatialization. */
50411  switch (ma_spatializer_get_attenuation_model(pSpatializer)) {
50412  case ma_attenuation_model_inverse:
50413  {
50414  gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff);
50415  } break;
50416  case ma_attenuation_model_linear:
50417  {
50418  gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff);
50419  } break;
50420  case ma_attenuation_model_exponential:
50421  {
50422  gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff);
50423  } break;
50424  case ma_attenuation_model_none:
50425  default:
50426  {
50427  gain = 1;
50428  } break;
50429  }
50430 
50431  /* Normalize the position. */
50432  if (distance > 0.001f) {
50433  float distanceInv = 1/distance;
50434  relativePosNormalized = relativePos;
50435  relativePosNormalized.x *= distanceInv;
50436  relativePosNormalized.y *= distanceInv;
50437  relativePosNormalized.z *= distanceInv;
50438  } else {
50439  distance = 0;
50440  relativePosNormalized = ma_vec3f_init_3f(0, 0, 0);
50441  }
50442 
50443  /*
50444  Angular attenuation.
50445 
50446  Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
50447  this out for ourselves at the expense of possibly being inconsistent with other implementations.
50448 
50449  To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
50450  just need to get the direction from the source to the listener and then do a dot product against that and the
50451  direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
50452  angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
50453  the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
50454  */
50455  if (distance > 0) {
50456  /* Source anglular gain. */
50457  float spatializerConeInnerAngle;
50458  float spatializerConeOuterAngle;
50459  float spatializerConeOuterGain;
50460  ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain);
50461 
50462  gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain);
50463 
50464  /*
50465  We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that
50466  are positioned behind the listener. On default settings, this will have no effect.
50467  */
50468  if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) {
50469  ma_vec3f listenerDirection;
50470  float listenerInnerAngle;
50471  float listenerOuterAngle;
50472  float listenerOuterGain;
50473 
50474  if (pListener->config.handedness == ma_handedness_right) {
50475  listenerDirection = ma_vec3f_init_3f(0, 0, -1);
50476  } else {
50477  listenerDirection = ma_vec3f_init_3f(0, 0, +1);
50478  }
50479 
50480  listenerInnerAngle = pListener->config.coneInnerAngleInRadians;
50481  listenerOuterAngle = pListener->config.coneOuterAngleInRadians;
50482  listenerOuterGain = pListener->config.coneOuterGain;
50483 
50484  gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain);
50485  }
50486  } else {
50487  /* The sound is right on top of the listener. Don't do any angular attenuation. */
50488  }
50489 
50490 
50491  /* Clamp the gain. */
50492  gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer));
50493 
50494  /*
50495  The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel
50496  gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions
50497  to avoid harsh changes in gain.
50498  */
50499  for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
50500  pSpatializer->pNewChannelGainsOut[iChannel] = gain;
50501  }
50502 
50503  /*
50504  Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore
50505  the whole section of code here because we need to update some internal spatialization state.
50506  */
50507  if (ma_spatializer_listener_is_enabled(pListener)) {
50508  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);
50509  } else {
50510  ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
50511  }
50512 
50513 
50514  /*
50515  Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for
50516  when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the
50517  gain to the final output.
50518  */
50519  /*printf("distance=%f; gain=%f\n", distance, gain);*/
50520 
50521  /* We must have a valid channel map here to ensure we spatialize properly. */
50522  MA_ASSERT(pChannelMapOut != NULL);
50523 
50524  /*
50525  We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being
50526  to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that
50527  the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and
50528  seeing how it goes. There might be better ways to do this.
50529 
50530  To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a
50531  direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will
50532  be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized
50533  position of the sound.
50534  */
50535 
50536  /*
50537  Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's
50538  relation to the direction of the channel.
50539  */
50540  if (distance > 0) {
50541  ma_vec3f unitPos = relativePos;
50542  float distanceInv = 1/distance;
50543  unitPos.x *= distanceInv;
50544  unitPos.y *= distanceInv;
50545  unitPos.z *= distanceInv;
50546 
50547  for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
50548  ma_channel channelOut;
50549  float d;
50550  float dMin;
50551 
50552  channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel);
50553  if (ma_is_spatial_channel_position(channelOut)) {
50554  d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer));
50555  } else {
50556  d = 1; /* It's not a spatial channel so there's no real notion of direction. */
50557  }
50558 
50559  /*
50560  In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable.
50561  The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to
50562  0, panning will be most extreme and any sounds that are positioned on the opposite side of the
50563  speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it
50564  doesn't even remotely represent the real world at all because sounds that come from your right side
50565  are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at
50566  all, which is also not ideal. By setting it to something greater than 0, the spatialization effect
50567  becomes much less dramatic and a lot more bearable.
50568 
50569  Summary: 0 = more extreme panning; 1 = no panning.
50570  */
50571  dMin = pSpatializer->minSpatializationChannelGain;
50572 
50573  /*
50574  At this point, "d" will be positive if the sound is on the same side as the channel and negative if
50575  it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to
50576  calculate a panning value. The first is to simply convert it to 0..1, however this has a problem
50577  which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right
50578  in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like
50579  the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front
50580  of the listener. I would intuitively expect that to be played at full volume, or close to it.
50581 
50582  The second idea I think of is to only apply a reduction in gain when the sound is on the opposite
50583  side of the speaker. That is, reduce the gain only when the dot product is negative. The problem
50584  with this is that there will not be any attenuation as the sound sweeps around the 180 degrees
50585  where the dot product is positive. The idea with this option is that you leave the gain at 1 when
50586  the sound is being played on the same side as the speaker and then you just reduce the volume when
50587  the sound is on the other side.
50588 
50589  The summarize, I think the first option should give a better sense of spatialization, but the second
50590  option is better for preserving the sound's power.
50591 
50592  UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a
50593  bit better, but you can also hear the reduction in volume when it's right in front.
50594  */
50595  #if 1
50596  {
50597  /*
50598  Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power
50599  by being played at 0.5 gain.
50600  */
50601  d = (d + 1) * 0.5f; /* -1..1 to 0..1 */
50602  d = ma_max(d, dMin);
50603  pSpatializer->pNewChannelGainsOut[iChannel] *= d;
50604  }
50605  #else
50606  {
50607  /*
50608  Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more
50609  consistent, but comes at the expense of a worse sense of space and positioning.
50610  */
50611  if (d < 0) {
50612  d += 1; /* Move into the positive range. */
50613  d = ma_max(d, dMin);
50614  channelGainsOut[iChannel] *= d;
50615  }
50616  }
50617  #endif
50618  }
50619  } else {
50620  /* Assume the sound is right on top of us. Don't do any panning. */
50621  }
50622 
50623  /* 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. */
50624  ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut);
50625  ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount);
50626 
50627  /*
50628  Before leaving we'll want to update our doppler pitch so that the caller can apply some
50629  pitch shifting if they desire. Note that we need to negate the relative position here
50630  because the doppler calculation needs to be source-to-listener, but ours is listener-to-
50631  source.
50632  */
50633  if (dopplerFactor > 0) {
50634  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);
50635  } else {
50636  pSpatializer->dopplerPitch = 1;
50637  }
50638  }
50639 
50640  return MA_SUCCESS;
50641 }
50642 
50643 MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume)
50644 {
50645  if (pSpatializer == NULL) {
50646  return MA_INVALID_ARGS;
50647  }
50648 
50649  return ma_gainer_set_master_volume(&pSpatializer->gainer, volume);
50650 }
50651 
50652 MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume)
50653 {
50654  if (pSpatializer == NULL) {
50655  return MA_INVALID_ARGS;
50656  }
50657 
50658  return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume);
50659 }
50660 
50661 MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer)
50662 {
50663  if (pSpatializer == NULL) {
50664  return 0;
50665  }
50666 
50667  return pSpatializer->channelsIn;
50668 }
50669 
50670 MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer)
50671 {
50672  if (pSpatializer == NULL) {
50673  return 0;
50674  }
50675 
50676  return pSpatializer->channelsOut;
50677 }
50678 
50679 MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel)
50680 {
50681  if (pSpatializer == NULL) {
50682  return;
50683  }
50684 
50685  ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel);
50686 }
50687 
50688 MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer)
50689 {
50690  if (pSpatializer == NULL) {
50691  return ma_attenuation_model_none;
50692  }
50693 
50694  return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel);
50695 }
50696 
50697 MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning)
50698 {
50699  if (pSpatializer == NULL) {
50700  return;
50701  }
50702 
50703  ma_atomic_exchange_i32(&pSpatializer->positioning, positioning);
50704 }
50705 
50706 MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer)
50707 {
50708  if (pSpatializer == NULL) {
50709  return ma_positioning_absolute;
50710  }
50711 
50712  return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning);
50713 }
50714 
50715 MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff)
50716 {
50717  if (pSpatializer == NULL) {
50718  return;
50719  }
50720 
50721  ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff);
50722 }
50723 
50724 MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer)
50725 {
50726  if (pSpatializer == NULL) {
50727  return 0;
50728  }
50729 
50730  return ma_atomic_load_f32(&pSpatializer->rolloff);
50731 }
50732 
50733 MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain)
50734 {
50735  if (pSpatializer == NULL) {
50736  return;
50737  }
50738 
50739  ma_atomic_exchange_f32(&pSpatializer->minGain, minGain);
50740 }
50741 
50742 MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer)
50743 {
50744  if (pSpatializer == NULL) {
50745  return 0;
50746  }
50747 
50748  return ma_atomic_load_f32(&pSpatializer->minGain);
50749 }
50750 
50751 MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain)
50752 {
50753  if (pSpatializer == NULL) {
50754  return;
50755  }
50756 
50757  ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain);
50758 }
50759 
50760 MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer)
50761 {
50762  if (pSpatializer == NULL) {
50763  return 0;
50764  }
50765 
50766  return ma_atomic_load_f32(&pSpatializer->maxGain);
50767 }
50768 
50769 MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance)
50770 {
50771  if (pSpatializer == NULL) {
50772  return;
50773  }
50774 
50775  ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance);
50776 }
50777 
50778 MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer)
50779 {
50780  if (pSpatializer == NULL) {
50781  return 0;
50782  }
50783 
50784  return ma_atomic_load_f32(&pSpatializer->minDistance);
50785 }
50786 
50787 MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance)
50788 {
50789  if (pSpatializer == NULL) {
50790  return;
50791  }
50792 
50793  ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance);
50794 }
50795 
50796 MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer)
50797 {
50798  if (pSpatializer == NULL) {
50799  return 0;
50800  }
50801 
50802  return ma_atomic_load_f32(&pSpatializer->maxDistance);
50803 }
50804 
50805 MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
50806 {
50807  if (pSpatializer == NULL) {
50808  return;
50809  }
50810 
50811  ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians);
50812  ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians);
50813  ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain);
50814 }
50815 
50816 MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
50817 {
50818  if (pSpatializer == NULL) {
50819  return;
50820  }
50821 
50822  if (pInnerAngleInRadians != NULL) {
50823  *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians);
50824  }
50825 
50826  if (pOuterAngleInRadians != NULL) {
50827  *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians);
50828  }
50829 
50830  if (pOuterGain != NULL) {
50831  *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain);
50832  }
50833 }
50834 
50835 MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor)
50836 {
50837  if (pSpatializer == NULL) {
50838  return;
50839  }
50840 
50841  ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor);
50842 }
50843 
50844 MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer)
50845 {
50846  if (pSpatializer == NULL) {
50847  return 1;
50848  }
50849 
50850  return ma_atomic_load_f32(&pSpatializer->dopplerFactor);
50851 }
50852 
50853 MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor)
50854 {
50855  if (pSpatializer == NULL) {
50856  return;
50857  }
50858 
50859  ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor);
50860 }
50861 
50862 MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer)
50863 {
50864  if (pSpatializer == NULL) {
50865  return 1;
50866  }
50867 
50868  return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor);
50869 }
50870 
50871 MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z)
50872 {
50873  if (pSpatializer == NULL) {
50874  return;
50875  }
50876 
50877  ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z));
50878 }
50879 
50880 MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer)
50881 {
50882  if (pSpatializer == NULL) {
50883  return ma_vec3f_init_3f(0, 0, 0);
50884  }
50885 
50886  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. */
50887 }
50888 
50889 MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z)
50890 {
50891  if (pSpatializer == NULL) {
50892  return;
50893  }
50894 
50895  ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z));
50896 }
50897 
50898 MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer)
50899 {
50900  if (pSpatializer == NULL) {
50901  return ma_vec3f_init_3f(0, 0, -1);
50902  }
50903 
50904  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. */
50905 }
50906 
50907 MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z)
50908 {
50909  if (pSpatializer == NULL) {
50910  return;
50911  }
50912 
50913  ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z));
50914 }
50915 
50916 MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer)
50917 {
50918  if (pSpatializer == NULL) {
50919  return ma_vec3f_init_3f(0, 0, 0);
50920  }
50921 
50922  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. */
50923 }
50924 
50925 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)
50926 {
50927  if (pRelativePos != NULL) {
50928  pRelativePos->x = 0;
50929  pRelativePos->y = 0;
50930  pRelativePos->z = 0;
50931  }
50932 
50933  if (pRelativeDir != NULL) {
50934  pRelativeDir->x = 0;
50935  pRelativeDir->y = 0;
50936  pRelativeDir->z = -1;
50937  }
50938 
50939  if (pSpatializer == NULL) {
50940  return;
50941  }
50942 
50943  if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
50944  /* There's no listener or we're using relative positioning. */
50945  if (pRelativePos != NULL) {
50946  *pRelativePos = ma_spatializer_get_position(pSpatializer);
50947  }
50948  if (pRelativeDir != NULL) {
50949  *pRelativeDir = ma_spatializer_get_direction(pSpatializer);
50950  }
50951  } else {
50952  ma_vec3f spatializerPosition;
50953  ma_vec3f spatializerDirection;
50954  ma_vec3f listenerPosition;
50955  ma_vec3f listenerDirection;
50956  ma_vec3f v;
50957  ma_vec3f axisX;
50958  ma_vec3f axisY;
50959  ma_vec3f axisZ;
50960  float m[4][4];
50961 
50962  spatializerPosition = ma_spatializer_get_position(pSpatializer);
50963  spatializerDirection = ma_spatializer_get_direction(pSpatializer);
50964  listenerPosition = ma_spatializer_listener_get_position(pListener);
50965  listenerDirection = ma_spatializer_listener_get_direction(pListener);
50966 
50967  /*
50968  We need to calcualte the right vector from our forward and up vectors. This is done with
50969  a cross product.
50970  */
50971  axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */
50972  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. */
50973 
50974  /*
50975  The calculation of axisX above can result in a zero-length vector if the listener is
50976  looking straight up on the Y axis. We'll need to fall back to a +X in this case so that
50977  the calculations below don't fall apart. This is where a quaternion based listener and
50978  sound orientation would come in handy.
50979  */
50980  if (ma_vec3f_len2(axisX) == 0) {
50981  axisX = ma_vec3f_init_3f(1, 0, 0);
50982  }
50983 
50984  axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */
50985 
50986  /*
50987  We need to swap the X axis if we're left handed because otherwise the cross product above
50988  will have resulted in it pointing in the wrong direction (right handed was assumed in the
50989  cross products above).
50990  */
50991  if (pListener->config.handedness == ma_handedness_left) {
50992  axisX = ma_vec3f_neg(axisX);
50993  }
50994 
50995  /* Lookat. */
50996  m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition);
50997  m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition);
50998  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);
50999  m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1;
51000 
51001  /*
51002  Multiply the lookat matrix by the spatializer position to transform it to listener
51003  space. This allows calculations to work based on the sound being relative to the
51004  origin which makes things simpler.
51005  */
51006  if (pRelativePos != NULL) {
51007  v = spatializerPosition;
51008  pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1;
51009  pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1;
51010  pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1;
51011  }
51012 
51013  /*
51014  The direction of the sound needs to also be transformed so that it's relative to the
51015  rotation of the listener.
51016  */
51017  if (pRelativeDir != NULL) {
51018  v = spatializerDirection;
51019  pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z;
51020  pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z;
51021  pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z;
51022  }
51023  }
51024 }
51025 
51026 
51027 
51028 
51029 /**************************************************************************************************************************************************************
51030 
51031 Resampling
51032 
51033 **************************************************************************************************************************************************************/
51034 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
51035 {
51037  MA_ZERO_OBJECT(&config);
51038  config.format = format;
51039  config.channels = channels;
51040  config.sampleRateIn = sampleRateIn;
51041  config.sampleRateOut = sampleRateOut;
51042  config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
51043  config.lpfNyquistFactor = 1;
51044 
51045  return config;
51046 }
51047 
51048 
51049 typedef struct
51050 {
51051  size_t sizeInBytes;
51052  size_t x0Offset;
51053  size_t x1Offset;
51054  size_t lpfOffset;
51055 } ma_linear_resampler_heap_layout;
51056 
51057 
51058 static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
51059 {
51060  /*
51061  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
51062  be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
51063  */
51064  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. */
51065  ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
51066 
51067  pResampler->inTimeFrac =
51068  (oldRateTimeWhole * newSampleRateOut) +
51069  ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut);
51070 
51071  /* Make sure the fractional part is less than the output sample rate. */
51072  pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
51073  pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
51074 }
51075 
51076 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)
51077 {
51078  ma_result result;
51079  ma_uint32 gcf;
51080  ma_uint32 lpfSampleRate;
51081  double lpfCutoffFrequency;
51082  ma_lpf_config lpfConfig;
51083  ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */
51084 
51085  if (pResampler == NULL) {
51086  return MA_INVALID_ARGS;
51087  }
51088 
51089  if (sampleRateIn == 0 || sampleRateOut == 0) {
51090  return MA_INVALID_ARGS;
51091  }
51092 
51093  oldSampleRateOut = pResampler->config.sampleRateOut;
51094 
51095  pResampler->config.sampleRateIn = sampleRateIn;
51096  pResampler->config.sampleRateOut = sampleRateOut;
51097 
51098  /* Simplify the sample rate. */
51099  gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
51100  pResampler->config.sampleRateIn /= gcf;
51101  pResampler->config.sampleRateOut /= gcf;
51102 
51103  /* Always initialize the low-pass filter, even when the order is 0. */
51104  if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {
51105  return MA_INVALID_ARGS;
51106  }
51107 
51108  lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
51109  lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
51110 
51111  lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
51112 
51113  /*
51114  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
51115  getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
51116  */
51117  if (isResamplerAlreadyInitialized) {
51118  result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
51119  } else {
51120  result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf);
51121  }
51122 
51123  if (result != MA_SUCCESS) {
51124  return result;
51125  }
51126 
51127 
51128  pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
51129  pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
51130 
51131  /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
51132  ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
51133 
51134  return MA_SUCCESS;
51135 }
51136 
51137 static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)
51138 {
51139  MA_ASSERT(pHeapLayout != NULL);
51140 
51141  MA_ZERO_OBJECT(pHeapLayout);
51142 
51143  if (pConfig == NULL) {
51144  return MA_INVALID_ARGS;
51145  }
51146 
51147  if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
51148  return MA_INVALID_ARGS;
51149  }
51150 
51151  if (pConfig->channels == 0) {
51152  return MA_INVALID_ARGS;
51153  }
51154 
51155  pHeapLayout->sizeInBytes = 0;
51156 
51157  /* x0 */
51158  pHeapLayout->x0Offset = pHeapLayout->sizeInBytes;
51159  if (pConfig->format == ma_format_f32) {
51160  pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
51161  } else {
51162  pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
51163  }
51164 
51165  /* x1 */
51166  pHeapLayout->x1Offset = pHeapLayout->sizeInBytes;
51167  if (pConfig->format == ma_format_f32) {
51168  pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
51169  } else {
51170  pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
51171  }
51172 
51173  /* LPF */
51174  pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes);
51175  {
51176  ma_result result;
51177  size_t lpfHeapSizeInBytes;
51178  ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */
51179 
51180  result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes);
51181  if (result != MA_SUCCESS) {
51182  return result;
51183  }
51184 
51185  pHeapLayout->sizeInBytes += lpfHeapSizeInBytes;
51186  }
51187 
51188  /* Make sure allocation size is aligned. */
51189  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
51190 
51191  return MA_SUCCESS;
51192 }
51193 
51194 MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes)
51195 {
51196  ma_result result;
51197  ma_linear_resampler_heap_layout heapLayout;
51198 
51199  if (pHeapSizeInBytes == NULL) {
51200  return MA_INVALID_ARGS;
51201  }
51202 
51203  *pHeapSizeInBytes = 0;
51204 
51205  result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
51206  if (result != MA_SUCCESS) {
51207  return result;
51208  }
51209 
51210  *pHeapSizeInBytes = heapLayout.sizeInBytes;
51211 
51212  return MA_SUCCESS;
51213 }
51214 
51215 MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler)
51216 {
51217  ma_result result;
51218  ma_linear_resampler_heap_layout heapLayout;
51219 
51220  if (pResampler == NULL) {
51221  return MA_INVALID_ARGS;
51222  }
51223 
51224  MA_ZERO_OBJECT(pResampler);
51225 
51226  result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
51227  if (result != MA_SUCCESS) {
51228  return result;
51229  }
51230 
51231  pResampler->config = *pConfig;
51232 
51233  pResampler->_pHeap = pHeap;
51234  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
51235 
51236  if (pConfig->format == ma_format_f32) {
51237  pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
51238  pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
51239  } else {
51240  pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
51241  pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
51242  }
51243 
51244  /* Setting the rate will set up the filter and time advances for us. */
51245  result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
51246  if (result != MA_SUCCESS) {
51247  return result;
51248  }
51249 
51250  pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
51251  pResampler->inTimeFrac = 0;
51252 
51253  return MA_SUCCESS;
51254 }
51255 
51256 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler)
51257 {
51258  ma_result result;
51259  size_t heapSizeInBytes;
51260  void* pHeap;
51261 
51262  result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes);
51263  if (result != MA_SUCCESS) {
51264  return result;
51265  }
51266 
51267  if (heapSizeInBytes > 0) {
51268  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
51269  if (pHeap == NULL) {
51270  return MA_OUT_OF_MEMORY;
51271  }
51272  } else {
51273  pHeap = NULL;
51274  }
51275 
51276  result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler);
51277  if (result != MA_SUCCESS) {
51278  ma_free(pHeap, pAllocationCallbacks);
51279  return result;
51280  }
51281 
51282  pResampler->_ownsHeap = MA_TRUE;
51283  return MA_SUCCESS;
51284 }
51285 
51286 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
51287 {
51288  if (pResampler == NULL) {
51289  return;
51290  }
51291 
51292  ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks);
51293 
51294  if (pResampler->_ownsHeap) {
51295  ma_free(pResampler->_pHeap, pAllocationCallbacks);
51296  }
51297 }
51298 
51299 static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
51300 {
51301  ma_int32 b;
51302  ma_int32 c;
51303  ma_int32 r;
51304 
51305  MA_ASSERT(a <= (1<<shift));
51306 
51307  b = x * ((1<<shift) - a);
51308  c = y * a;
51309  r = b + c;
51310 
51311  return (ma_int16)(r >> shift);
51312 }
51313 
51314 static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)
51315 {
51316  ma_uint32 c;
51317  ma_uint32 a;
51318  const ma_uint32 channels = pResampler->config.channels;
51319  const ma_uint32 shift = 12;
51320 
51321  MA_ASSERT(pResampler != NULL);
51322  MA_ASSERT(pFrameOut != NULL);
51323 
51324  a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
51325 
51326  MA_ASSUME(channels > 0);
51327  for (c = 0; c < channels; c += 1) {
51328  ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
51329  pFrameOut[c] = s;
51330  }
51331 }
51332 
51333 
51334 static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut)
51335 {
51336  ma_uint32 c;
51337  float a;
51338  const ma_uint32 channels = pResampler->config.channels;
51339 
51340  MA_ASSERT(pResampler != NULL);
51341  MA_ASSERT(pFrameOut != NULL);
51342 
51343  a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
51344 
51345  MA_ASSUME(channels > 0);
51346  for (c = 0; c < channels; c += 1) {
51347  float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
51348  pFrameOut[c] = s;
51349  }
51350 }
51351 
51352 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)
51353 {
51354  const ma_int16* pFramesInS16;
51355  /* */ ma_int16* pFramesOutS16;
51356  ma_uint64 frameCountIn;
51357  ma_uint64 frameCountOut;
51358  ma_uint64 framesProcessedIn;
51359  ma_uint64 framesProcessedOut;
51360 
51361  MA_ASSERT(pResampler != NULL);
51362  MA_ASSERT(pFrameCountIn != NULL);
51363  MA_ASSERT(pFrameCountOut != NULL);
51364 
51365  pFramesInS16 = (const ma_int16*)pFramesIn;
51366  pFramesOutS16 = ( ma_int16*)pFramesOut;
51367  frameCountIn = *pFrameCountIn;
51368  frameCountOut = *pFrameCountOut;
51369  framesProcessedIn = 0;
51370  framesProcessedOut = 0;
51371 
51372  while (framesProcessedOut < frameCountOut) {
51373  /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
51374  while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
51375  ma_uint32 iChannel;
51376 
51377  if (pFramesInS16 != NULL) {
51378  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51379  pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
51380  pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
51381  }
51382  pFramesInS16 += pResampler->config.channels;
51383  } else {
51384  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51385  pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
51386  pResampler->x1.s16[iChannel] = 0;
51387  }
51388  }
51389 
51390  /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
51391  if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
51392  ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);
51393  }
51394 
51395  framesProcessedIn += 1;
51396  pResampler->inTimeInt -= 1;
51397  }
51398 
51399  if (pResampler->inTimeInt > 0) {
51400  break; /* Ran out of input data. */
51401  }
51402 
51403  /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
51404  if (pFramesOutS16 != NULL) {
51405  MA_ASSERT(pResampler->inTimeInt == 0);
51406  ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
51407 
51408  pFramesOutS16 += pResampler->config.channels;
51409  }
51410 
51411  framesProcessedOut += 1;
51412 
51413  /* Advance time forward. */
51414  pResampler->inTimeInt += pResampler->inAdvanceInt;
51415  pResampler->inTimeFrac += pResampler->inAdvanceFrac;
51416  if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
51417  pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
51418  pResampler->inTimeInt += 1;
51419  }
51420  }
51421 
51422  *pFrameCountIn = framesProcessedIn;
51423  *pFrameCountOut = framesProcessedOut;
51424 
51425  return MA_SUCCESS;
51426 }
51427 
51428 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)
51429 {
51430  const ma_int16* pFramesInS16;
51431  /* */ ma_int16* pFramesOutS16;
51432  ma_uint64 frameCountIn;
51433  ma_uint64 frameCountOut;
51434  ma_uint64 framesProcessedIn;
51435  ma_uint64 framesProcessedOut;
51436 
51437  MA_ASSERT(pResampler != NULL);
51438  MA_ASSERT(pFrameCountIn != NULL);
51439  MA_ASSERT(pFrameCountOut != NULL);
51440 
51441  pFramesInS16 = (const ma_int16*)pFramesIn;
51442  pFramesOutS16 = ( ma_int16*)pFramesOut;
51443  frameCountIn = *pFrameCountIn;
51444  frameCountOut = *pFrameCountOut;
51445  framesProcessedIn = 0;
51446  framesProcessedOut = 0;
51447 
51448  while (framesProcessedOut < frameCountOut) {
51449  /* Before interpolating we need to load the buffers. */
51450  while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
51451  ma_uint32 iChannel;
51452 
51453  if (pFramesInS16 != NULL) {
51454  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51455  pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
51456  pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
51457  }
51458  pFramesInS16 += pResampler->config.channels;
51459  } else {
51460  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51461  pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
51462  pResampler->x1.s16[iChannel] = 0;
51463  }
51464  }
51465 
51466  framesProcessedIn += 1;
51467  pResampler->inTimeInt -= 1;
51468  }
51469 
51470  if (pResampler->inTimeInt > 0) {
51471  break; /* Ran out of input data. */
51472  }
51473 
51474  /* Getting here means the frames have been loaded and we can generate the next output frame. */
51475  if (pFramesOutS16 != NULL) {
51476  MA_ASSERT(pResampler->inTimeInt == 0);
51477  ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
51478 
51479  /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
51480  if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
51481  ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);
51482  }
51483 
51484  pFramesOutS16 += pResampler->config.channels;
51485  }
51486 
51487  framesProcessedOut += 1;
51488 
51489  /* Advance time forward. */
51490  pResampler->inTimeInt += pResampler->inAdvanceInt;
51491  pResampler->inTimeFrac += pResampler->inAdvanceFrac;
51492  if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
51493  pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
51494  pResampler->inTimeInt += 1;
51495  }
51496  }
51497 
51498  *pFrameCountIn = framesProcessedIn;
51499  *pFrameCountOut = framesProcessedOut;
51500 
51501  return MA_SUCCESS;
51502 }
51503 
51504 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)
51505 {
51506  MA_ASSERT(pResampler != NULL);
51507 
51508  if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
51509  return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51510  } else {
51511  return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51512  }
51513 }
51514 
51515 
51516 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)
51517 {
51518  const float* pFramesInF32;
51519  /* */ float* pFramesOutF32;
51520  ma_uint64 frameCountIn;
51521  ma_uint64 frameCountOut;
51522  ma_uint64 framesProcessedIn;
51523  ma_uint64 framesProcessedOut;
51524 
51525  MA_ASSERT(pResampler != NULL);
51526  MA_ASSERT(pFrameCountIn != NULL);
51527  MA_ASSERT(pFrameCountOut != NULL);
51528 
51529  pFramesInF32 = (const float*)pFramesIn;
51530  pFramesOutF32 = ( float*)pFramesOut;
51531  frameCountIn = *pFrameCountIn;
51532  frameCountOut = *pFrameCountOut;
51533  framesProcessedIn = 0;
51534  framesProcessedOut = 0;
51535 
51536  while (framesProcessedOut < frameCountOut) {
51537  /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
51538  while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
51539  ma_uint32 iChannel;
51540 
51541  if (pFramesInF32 != NULL) {
51542  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51543  pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
51544  pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
51545  }
51546  pFramesInF32 += pResampler->config.channels;
51547  } else {
51548  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51549  pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
51550  pResampler->x1.f32[iChannel] = 0;
51551  }
51552  }
51553 
51554  /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
51555  if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
51556  ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);
51557  }
51558 
51559  framesProcessedIn += 1;
51560  pResampler->inTimeInt -= 1;
51561  }
51562 
51563  if (pResampler->inTimeInt > 0) {
51564  break; /* Ran out of input data. */
51565  }
51566 
51567  /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
51568  if (pFramesOutF32 != NULL) {
51569  MA_ASSERT(pResampler->inTimeInt == 0);
51570  ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
51571 
51572  pFramesOutF32 += pResampler->config.channels;
51573  }
51574 
51575  framesProcessedOut += 1;
51576 
51577  /* Advance time forward. */
51578  pResampler->inTimeInt += pResampler->inAdvanceInt;
51579  pResampler->inTimeFrac += pResampler->inAdvanceFrac;
51580  if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
51581  pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
51582  pResampler->inTimeInt += 1;
51583  }
51584  }
51585 
51586  *pFrameCountIn = framesProcessedIn;
51587  *pFrameCountOut = framesProcessedOut;
51588 
51589  return MA_SUCCESS;
51590 }
51591 
51592 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)
51593 {
51594  const float* pFramesInF32;
51595  /* */ float* pFramesOutF32;
51596  ma_uint64 frameCountIn;
51597  ma_uint64 frameCountOut;
51598  ma_uint64 framesProcessedIn;
51599  ma_uint64 framesProcessedOut;
51600 
51601  MA_ASSERT(pResampler != NULL);
51602  MA_ASSERT(pFrameCountIn != NULL);
51603  MA_ASSERT(pFrameCountOut != NULL);
51604 
51605  pFramesInF32 = (const float*)pFramesIn;
51606  pFramesOutF32 = ( float*)pFramesOut;
51607  frameCountIn = *pFrameCountIn;
51608  frameCountOut = *pFrameCountOut;
51609  framesProcessedIn = 0;
51610  framesProcessedOut = 0;
51611 
51612  while (framesProcessedOut < frameCountOut) {
51613  /* Before interpolating we need to load the buffers. */
51614  while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
51615  ma_uint32 iChannel;
51616 
51617  if (pFramesInF32 != NULL) {
51618  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51619  pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
51620  pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
51621  }
51622  pFramesInF32 += pResampler->config.channels;
51623  } else {
51624  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51625  pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
51626  pResampler->x1.f32[iChannel] = 0;
51627  }
51628  }
51629 
51630  framesProcessedIn += 1;
51631  pResampler->inTimeInt -= 1;
51632  }
51633 
51634  if (pResampler->inTimeInt > 0) {
51635  break; /* Ran out of input data. */
51636  }
51637 
51638  /* Getting here means the frames have been loaded and we can generate the next output frame. */
51639  if (pFramesOutF32 != NULL) {
51640  MA_ASSERT(pResampler->inTimeInt == 0);
51641  ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
51642 
51643  /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
51644  if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
51645  ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);
51646  }
51647 
51648  pFramesOutF32 += pResampler->config.channels;
51649  }
51650 
51651  framesProcessedOut += 1;
51652 
51653  /* Advance time forward. */
51654  pResampler->inTimeInt += pResampler->inAdvanceInt;
51655  pResampler->inTimeFrac += pResampler->inAdvanceFrac;
51656  if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
51657  pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
51658  pResampler->inTimeInt += 1;
51659  }
51660  }
51661 
51662  *pFrameCountIn = framesProcessedIn;
51663  *pFrameCountOut = framesProcessedOut;
51664 
51665  return MA_SUCCESS;
51666 }
51667 
51668 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)
51669 {
51670  MA_ASSERT(pResampler != NULL);
51671 
51672  if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
51673  return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51674  } else {
51675  return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51676  }
51677 }
51678 
51679 
51680 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)
51681 {
51682  if (pResampler == NULL) {
51683  return MA_INVALID_ARGS;
51684  }
51685 
51686  /* */ if (pResampler->config.format == ma_format_s16) {
51687  return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51688  } else if (pResampler->config.format == ma_format_f32) {
51689  return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51690  } else {
51691  /* 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(). */
51692  MA_ASSERT(MA_FALSE);
51693  return MA_INVALID_ARGS;
51694  }
51695 }
51696 
51697 
51698 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
51699 {
51700  return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
51701 }
51702 
51703 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)
51704 {
51705  ma_uint32 n;
51706  ma_uint32 d;
51707 
51708  if (pResampler == NULL) {
51709  return MA_INVALID_ARGS;
51710  }
51711 
51712  if (ratioInOut <= 0) {
51713  return MA_INVALID_ARGS;
51714  }
51715 
51716  d = 1000000;
51717  n = (ma_uint32)(ratioInOut * d);
51718 
51719  if (n == 0) {
51720  return MA_INVALID_ARGS; /* Ratio too small. */
51721  }
51722 
51723  MA_ASSERT(n != 0);
51724 
51725  return ma_linear_resampler_set_rate(pResampler, n, d);
51726 }
51727 
51728 MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler)
51729 {
51730  if (pResampler == NULL) {
51731  return 0;
51732  }
51733 
51734  return 1 + ma_lpf_get_latency(&pResampler->lpf);
51735 }
51736 
51737 MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler)
51738 {
51739  if (pResampler == NULL) {
51740  return 0;
51741  }
51742 
51743  return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
51744 }
51745 
51746 MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
51747 {
51748  ma_uint64 inputFrameCount;
51749 
51750  if (pInputFrameCount == NULL) {
51751  return MA_INVALID_ARGS;
51752  }
51753 
51754  *pInputFrameCount = 0;
51755 
51756  if (pResampler == NULL) {
51757  return MA_INVALID_ARGS;
51758  }
51759 
51760  if (outputFrameCount == 0) {
51761  return MA_SUCCESS;
51762  }
51763 
51764  /* Any whole input frames are consumed before the first output frame is generated. */
51765  inputFrameCount = pResampler->inTimeInt;
51766  outputFrameCount -= 1;
51767 
51768  /* The rest of the output frames can be calculated in constant time. */
51769  inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;
51770  inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
51771 
51772  *pInputFrameCount = inputFrameCount;
51773 
51774  return MA_SUCCESS;
51775 }
51776 
51777 MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
51778 {
51779  ma_uint64 outputFrameCount;
51780  ma_uint64 preliminaryInputFrameCountFromFrac;
51781  ma_uint64 preliminaryInputFrameCount;
51782 
51783  if (pOutputFrameCount == NULL) {
51784  return MA_INVALID_ARGS;
51785  }
51786 
51787  *pOutputFrameCount = 0;
51788 
51789  if (pResampler == NULL) {
51790  return MA_INVALID_ARGS;
51791  }
51792 
51793  /*
51794  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
51795  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
51796  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
51797  of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.
51798  */
51799  outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn;
51800 
51801  /*
51802  We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is
51803  used in the logic below to determine whether or not we need to add an extra output frame.
51804  */
51805  preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut;
51806  preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
51807 
51808  /*
51809  If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than
51810  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
51811  to actually process. Otherwise we need to add the extra output frame.
51812  */
51813  if (preliminaryInputFrameCount <= inputFrameCount) {
51814  outputFrameCount += 1;
51815  }
51816 
51817  *pOutputFrameCount = outputFrameCount;
51818 
51819  return MA_SUCCESS;
51820 }
51821 
51822 MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
51823 {
51824  ma_uint32 iChannel;
51825 
51826  if (pResampler == NULL) {
51827  return MA_INVALID_ARGS;
51828  }
51829 
51830  /* Timers need to be cleared back to zero. */
51831  pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
51832  pResampler->inTimeFrac = 0;
51833 
51834  /* Cached samples need to be cleared. */
51835  if (pResampler->config.format == ma_format_f32) {
51836  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51837  pResampler->x0.f32[iChannel] = 0;
51838  pResampler->x1.f32[iChannel] = 0;
51839  }
51840  } else {
51841  for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51842  pResampler->x0.s16[iChannel] = 0;
51843  pResampler->x1.s16[iChannel] = 0;
51844  }
51845  }
51846 
51847  /* The low pass filter needs to have it's cache reset. */
51848  ma_lpf_clear_cache(&pResampler->lpf);
51849 
51850  return MA_SUCCESS;
51851 }
51852 
51853 
51854 
51855 /* Linear resampler backend vtable. */
51856 static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig)
51857 {
51858  ma_linear_resampler_config linearConfig;
51859 
51860  linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
51861  linearConfig.lpfOrder = pConfig->linear.lpfOrder;
51862 
51863  return linearConfig;
51864 }
51865 
51866 static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
51867 {
51868  ma_linear_resampler_config linearConfig;
51869 
51870  (void)pUserData;
51871 
51872  linearConfig = ma_resampling_backend_get_config__linear(pConfig);
51873 
51874  return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes);
51875 }
51876 
51877 static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend)
51878 {
51879  ma_resampler* pResampler = (ma_resampler*)pUserData;
51880  ma_result result;
51881  ma_linear_resampler_config linearConfig;
51882 
51883  (void)pUserData;
51884 
51885  linearConfig = ma_resampling_backend_get_config__linear(pConfig);
51886 
51887  result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear);
51888  if (result != MA_SUCCESS) {
51889  return result;
51890  }
51891 
51892  *ppBackend = &pResampler->state.linear;
51893 
51894  return MA_SUCCESS;
51895 }
51896 
51897 static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
51898 {
51899  (void)pUserData;
51900 
51901  ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks);
51902 }
51903 
51904 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)
51905 {
51906  (void)pUserData;
51907 
51908  return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51909 }
51910 
51911 static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
51912 {
51913  (void)pUserData;
51914 
51915  return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut);
51916 }
51917 
51918 static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
51919 {
51920  (void)pUserData;
51921 
51922  return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend);
51923 }
51924 
51925 static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
51926 {
51927  (void)pUserData;
51928 
51929  return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend);
51930 }
51931 
51932 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)
51933 {
51934  (void)pUserData;
51935 
51936  return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount);
51937 }
51938 
51939 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)
51940 {
51941  (void)pUserData;
51942 
51943  return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount);
51944 }
51945 
51946 static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend)
51947 {
51948  (void)pUserData;
51949 
51950  return ma_linear_resampler_reset((ma_linear_resampler*)pBackend);
51951 }
51952 
51953 static ma_resampling_backend_vtable g_ma_linear_resampler_vtable =
51954 {
51955  ma_resampling_backend_get_heap_size__linear,
51956  ma_resampling_backend_init__linear,
51957  ma_resampling_backend_uninit__linear,
51958  ma_resampling_backend_process__linear,
51959  ma_resampling_backend_set_rate__linear,
51960  ma_resampling_backend_get_input_latency__linear,
51961  ma_resampling_backend_get_output_latency__linear,
51962  ma_resampling_backend_get_required_input_frame_count__linear,
51963  ma_resampling_backend_get_expected_output_frame_count__linear,
51964  ma_resampling_backend_reset__linear
51965 };
51966 
51967 
51968 
51969 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)
51970 {
51971  ma_resampler_config config;
51972 
51973  MA_ZERO_OBJECT(&config);
51974  config.format = format;
51975  config.channels = channels;
51976  config.sampleRateIn = sampleRateIn;
51977  config.sampleRateOut = sampleRateOut;
51978  config.algorithm = algorithm;
51979 
51980  /* Linear. */
51981  config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
51982 
51983  return config;
51984 }
51985 
51986 static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData)
51987 {
51988  MA_ASSERT(pConfig != NULL);
51989  MA_ASSERT(ppVTable != NULL);
51990  MA_ASSERT(ppUserData != NULL);
51991 
51992  /* Safety. */
51993  *ppVTable = NULL;
51994  *ppUserData = NULL;
51995 
51996  switch (pConfig->algorithm)
51997  {
51998  case ma_resample_algorithm_linear:
51999  {
52000  *ppVTable = &g_ma_linear_resampler_vtable;
52001  *ppUserData = pResampler;
52002  } break;
52003 
52004  case ma_resample_algorithm_custom:
52005  {
52006  *ppVTable = pConfig->pBackendVTable;
52007  *ppUserData = pConfig->pBackendUserData;
52008  } break;
52009 
52010  default: return MA_INVALID_ARGS;
52011  }
52012 
52013  return MA_SUCCESS;
52014 }
52015 
52016 MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
52017 {
52018  ma_result result;
52020  void* pVTableUserData;
52021 
52022  if (pHeapSizeInBytes == NULL) {
52023  return MA_INVALID_ARGS;
52024  }
52025 
52026  *pHeapSizeInBytes = 0;
52027 
52028  if (pConfig == NULL) {
52029  return MA_INVALID_ARGS;
52030  }
52031 
52032  result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData);
52033  if (result != MA_SUCCESS) {
52034  return result;
52035  }
52036 
52037  if (pVTable == NULL || pVTable->onGetHeapSize == NULL) {
52038  return MA_NOT_IMPLEMENTED;
52039  }
52040 
52041  result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes);
52042  if (result != MA_SUCCESS) {
52043  return result;
52044  }
52045 
52046  return MA_SUCCESS;
52047 }
52048 
52049 MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler)
52050 {
52051  ma_result result;
52052 
52053  if (pResampler == NULL) {
52054  return MA_INVALID_ARGS;
52055  }
52056 
52057  MA_ZERO_OBJECT(pResampler);
52058 
52059  if (pConfig == NULL) {
52060  return MA_INVALID_ARGS;
52061  }
52062 
52063  pResampler->_pHeap = pHeap;
52064  pResampler->format = pConfig->format;
52065  pResampler->channels = pConfig->channels;
52066  pResampler->sampleRateIn = pConfig->sampleRateIn;
52067  pResampler->sampleRateOut = pConfig->sampleRateOut;
52068 
52069  result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData);
52070  if (result != MA_SUCCESS) {
52071  return result;
52072  }
52073 
52074  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) {
52075  return MA_NOT_IMPLEMENTED; /* onInit not implemented. */
52076  }
52077 
52078  result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend);
52079  if (result != MA_SUCCESS) {
52080  return result;
52081  }
52082 
52083  return MA_SUCCESS;
52084 }
52085 
52086 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler)
52087 {
52088  ma_result result;
52089  size_t heapSizeInBytes;
52090  void* pHeap;
52091 
52092  result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes);
52093  if (result != MA_SUCCESS) {
52094  return result;
52095  }
52096 
52097  if (heapSizeInBytes > 0) {
52098  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
52099  if (pHeap == NULL) {
52100  return MA_OUT_OF_MEMORY;
52101  }
52102  } else {
52103  pHeap = NULL;
52104  }
52105 
52106  result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler);
52107  if (result != MA_SUCCESS) {
52108  ma_free(pHeap, pAllocationCallbacks);
52109  return result;
52110  }
52111 
52112  pResampler->_ownsHeap = MA_TRUE;
52113  return MA_SUCCESS;
52114 }
52115 
52116 MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
52117 {
52118  if (pResampler == NULL) {
52119  return;
52120  }
52121 
52122  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) {
52123  return;
52124  }
52125 
52126  pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks);
52127 
52128  if (pResampler->_ownsHeap) {
52129  ma_free(pResampler->_pHeap, pAllocationCallbacks);
52130  }
52131 }
52132 
52133 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
52134 {
52135  if (pResampler == NULL) {
52136  return MA_INVALID_ARGS;
52137  }
52138 
52139  if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
52140  return MA_INVALID_ARGS;
52141  }
52142 
52143  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) {
52144  return MA_NOT_IMPLEMENTED;
52145  }
52146 
52147  return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52148 }
52149 
52150 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
52151 {
52152  ma_result result;
52153 
52154  if (pResampler == NULL) {
52155  return MA_INVALID_ARGS;
52156  }
52157 
52158  if (sampleRateIn == 0 || sampleRateOut == 0) {
52159  return MA_INVALID_ARGS;
52160  }
52161 
52162  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) {
52163  return MA_NOT_IMPLEMENTED;
52164  }
52165 
52166  result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut);
52167  if (result != MA_SUCCESS) {
52168  return result;
52169  }
52170 
52171  pResampler->sampleRateIn = sampleRateIn;
52172  pResampler->sampleRateOut = sampleRateOut;
52173 
52174  return MA_SUCCESS;
52175 }
52176 
52177 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)
52178 {
52179  ma_uint32 n;
52180  ma_uint32 d;
52181 
52182  if (pResampler == NULL) {
52183  return MA_INVALID_ARGS;
52184  }
52185 
52186  if (ratio <= 0) {
52187  return MA_INVALID_ARGS;
52188  }
52189 
52190  d = 1000;
52191  n = (ma_uint32)(ratio * d);
52192 
52193  if (n == 0) {
52194  return MA_INVALID_ARGS; /* Ratio too small. */
52195  }
52196 
52197  MA_ASSERT(n != 0);
52198 
52199  return ma_resampler_set_rate(pResampler, n, d);
52200 }
52201 
52202 MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler)
52203 {
52204  if (pResampler == NULL) {
52205  return 0;
52206  }
52207 
52208  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) {
52209  return 0;
52210  }
52211 
52212  return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend);
52213 }
52214 
52215 MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)
52216 {
52217  if (pResampler == NULL) {
52218  return 0;
52219  }
52220 
52221  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) {
52222  return 0;
52223  }
52224 
52225  return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend);
52226 }
52227 
52228 MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
52229 {
52230  if (pInputFrameCount == NULL) {
52231  return MA_INVALID_ARGS;
52232  }
52233 
52234  *pInputFrameCount = 0;
52235 
52236  if (pResampler == NULL) {
52237  return MA_INVALID_ARGS;
52238  }
52239 
52240  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) {
52241  return MA_NOT_IMPLEMENTED;
52242  }
52243 
52244  return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount);
52245 }
52246 
52247 MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
52248 {
52249  if (pOutputFrameCount == NULL) {
52250  return MA_INVALID_ARGS;
52251  }
52252 
52253  *pOutputFrameCount = 0;
52254 
52255  if (pResampler == NULL) {
52256  return MA_INVALID_ARGS;
52257  }
52258 
52259  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) {
52260  return MA_NOT_IMPLEMENTED;
52261  }
52262 
52263  return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount);
52264 }
52265 
52266 MA_API ma_result ma_resampler_reset(ma_resampler* pResampler)
52267 {
52268  if (pResampler == NULL) {
52269  return MA_INVALID_ARGS;
52270  }
52271 
52272  if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) {
52273  return MA_NOT_IMPLEMENTED;
52274  }
52275 
52276  return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend);
52277 }
52278 
52279 /**************************************************************************************************************************************************************
52280 
52281 Channel Conversion
52282 
52283 **************************************************************************************************************************************************************/
52284 #ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
52285 #define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
52286 #endif
52287 
52288 #define MA_PLANE_LEFT 0
52289 #define MA_PLANE_RIGHT 1
52290 #define MA_PLANE_FRONT 2
52291 #define MA_PLANE_BACK 3
52292 #define MA_PLANE_BOTTOM 4
52293 #define MA_PLANE_TOP 5
52294 
52295 static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
52296  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
52297  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
52298  { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
52299  { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
52300  { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
52301  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
52302  { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
52303  { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
52304  { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
52305  { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
52306  { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
52307  { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
52308  { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
52309  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
52310  { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
52311  { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
52312  { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
52313  { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
52314  { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
52315  { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
52316  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
52317  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
52318  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
52319  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
52320  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
52321  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
52322  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
52323  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
52324  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
52325  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
52326  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
52327  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
52328  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
52329  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
52330  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
52331  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
52332  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
52333  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
52334  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
52335  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
52336  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
52337  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
52338  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
52339  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
52340  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
52341  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
52342  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
52343  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
52344  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
52345  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
52346  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
52347  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
52348 };
52349 
52350 static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
52351 {
52352  /*
52353  Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
52354  the following output configuration:
52355 
52356  - front/left
52357  - side/left
52358  - back/left
52359 
52360  The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
52361  of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
52362 
52363  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
52364  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
52365  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
52366  receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
52367  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
52368  across 3 spatial dimensions.
52369 
52370  The first thing to do is figure out how each speaker's volume is spread over each of plane:
52371  - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
52372  - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
52373  - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
52374  - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
52375 
52376  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
52377  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
52378  taken by the other to produce the final contribution.
52379  */
52380 
52381  /* Contribution = Sum(Volume to Give * Volume to Take) */
52382  float contribution =
52383  g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
52384  g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
52385  g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
52386  g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
52387  g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
52388  g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
52389 
52390  return contribution;
52391 }
52392 
52393 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)
52394 {
52396 
52397  MA_ZERO_OBJECT(&config);
52398  config.format = format;
52399  config.channelsIn = channelsIn;
52400  config.channelsOut = channelsOut;
52401  config.pChannelMapIn = pChannelMapIn;
52402  config.pChannelMapOut = pChannelMapOut;
52403  config.mixingMode = mixingMode;
52404 
52405  return config;
52406 }
52407 
52408 static ma_int32 ma_channel_converter_float_to_fixed(float x)
52409 {
52410  return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
52411 }
52412 
52413 static ma_uint32 ma_channel_map_get_spatial_channel_count(const ma_channel* pChannelMap, ma_uint32 channels)
52414 {
52415  ma_uint32 spatialChannelCount = 0;
52416  ma_uint32 iChannel;
52417 
52418  MA_ASSERT(pChannelMap != NULL);
52419  MA_ASSERT(channels > 0);
52420 
52421  for (iChannel = 0; iChannel < channels; ++iChannel) {
52422  if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) {
52423  spatialChannelCount++;
52424  }
52425  }
52426 
52427  return spatialChannelCount;
52428 }
52429 
52430 static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
52431 {
52432  int i;
52433 
52434  if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
52435  return MA_FALSE;
52436  }
52437 
52438  if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) {
52439  return MA_FALSE;
52440  }
52441 
52442  for (i = 0; i < 6; ++i) { /* Each side of a cube. */
52443  if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
52444  return MA_TRUE;
52445  }
52446  }
52447 
52448  return MA_FALSE;
52449 }
52450 
52451 
52452 static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut)
52453 {
52454  if (channelsOut == channelsIn) {
52455  return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut);
52456  } else {
52457  return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */
52458  }
52459 }
52460 
52461 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)
52462 {
52463  if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) {
52464  return ma_channel_conversion_path_passthrough;
52465  }
52466 
52467  if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) {
52468  return ma_channel_conversion_path_mono_out;
52469  }
52470 
52471  if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) {
52472  return ma_channel_conversion_path_mono_in;
52473  }
52474 
52475  if (mode == ma_channel_mix_mode_custom_weights) {
52476  return ma_channel_conversion_path_weights;
52477  }
52478 
52479  /*
52480  We can use a simple shuffle if both channel maps have the same channel count and all channel
52481  positions are present in both.
52482  */
52483  if (channelsIn == channelsOut) {
52484  ma_uint32 iChannelIn;
52485  ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
52486  for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) {
52487  ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
52488  if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) {
52489  isInputChannelPositionInOutput = MA_TRUE;
52490  break;
52491  }
52492 
52493  if (!isInputChannelPositionInOutput) {
52494  areAllChannelPositionsPresent = MA_FALSE;
52495  break;
52496  }
52497  }
52498 
52499  if (areAllChannelPositionsPresent) {
52500  return ma_channel_conversion_path_shuffle;
52501  }
52502  }
52503 
52504  /* Getting here means we'll need to use weights. */
52505  return ma_channel_conversion_path_weights;
52506 }
52507 
52508 
52509 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)
52510 {
52511  ma_uint32 iChannelIn;
52512  ma_uint32 iChannelOut;
52513 
52514  if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) {
52515  return MA_INVALID_ARGS;
52516  }
52517 
52518  /*
52519  When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the
52520  input channel has more than one occurance of a channel position, the second one will be ignored.
52521  */
52522  for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) {
52523  ma_channel channelOut;
52524 
52525  /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */
52526  pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL;
52527 
52528  channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut);
52529  for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) {
52530  ma_channel channelIn;
52531 
52532  channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn);
52533  if (channelOut == channelIn) {
52534  pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
52535  break;
52536  }
52537 
52538  /*
52539  Getting here means the channels don't exactly match, but we are going to support some
52540  relaxed matching for practicality. If, for example, there are two stereo channel maps,
52541  but one uses front left/right and the other uses side left/right, it makes logical
52542  sense to just map these. The way we'll do it is we'll check if there is a logical
52543  corresponding mapping, and if so, apply it, but we will *not* break from the loop,
52544  thereby giving the loop a chance to find an exact match later which will take priority.
52545  */
52546  switch (channelOut)
52547  {
52548  /* Left channels. */
52549  case MA_CHANNEL_FRONT_LEFT:
52550  case MA_CHANNEL_SIDE_LEFT:
52551  {
52552  switch (channelIn) {
52553  case MA_CHANNEL_FRONT_LEFT:
52554  case MA_CHANNEL_SIDE_LEFT:
52555  {
52556  pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
52557  } break;
52558  }
52559  } break;
52560 
52561  /* Right channels. */
52562  case MA_CHANNEL_FRONT_RIGHT:
52563  case MA_CHANNEL_SIDE_RIGHT:
52564  {
52565  switch (channelIn) {
52566  case MA_CHANNEL_FRONT_RIGHT:
52567  case MA_CHANNEL_SIDE_RIGHT:
52568  {
52569  pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
52570  } break;
52571  }
52572  } break;
52573 
52574  default: break;
52575  }
52576  }
52577  }
52578 
52579  return MA_SUCCESS;
52580 }
52581 
52582 
52583 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)
52584 {
52585  ma_uint64 iFrame;
52586  ma_uint32 iChannelOut;
52587 
52588  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52589  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52590  ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52591  if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52592  pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
52593  } else {
52594  pFramesOut[iChannelOut] = 0;
52595  }
52596  }
52597 
52598  pFramesOut += channelsOut;
52599  pFramesIn += channelsIn;
52600  }
52601 }
52602 
52603 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)
52604 {
52605  ma_uint64 iFrame;
52606  ma_uint32 iChannelOut;
52607 
52608  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52609  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52610  ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52611  if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52612  pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
52613  } else {
52614  pFramesOut[iChannelOut] = 0;
52615  }
52616  }
52617 
52618  pFramesOut += channelsOut;
52619  pFramesIn += channelsIn;
52620  }
52621 }
52622 
52623 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)
52624 {
52625  ma_uint64 iFrame;
52626  ma_uint32 iChannelOut;
52627 
52628  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52629  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52630  ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52631  if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52632  pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0];
52633  pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1];
52634  pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2];
52635  } else {
52636  pFramesOut[iChannelOut*3 + 0] = 0;
52637  } pFramesOut[iChannelOut*3 + 1] = 0;
52638  } pFramesOut[iChannelOut*3 + 2] = 0;
52639 
52640  pFramesOut += channelsOut*3;
52641  pFramesIn += channelsIn*3;
52642  }
52643 }
52644 
52645 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)
52646 {
52647  ma_uint64 iFrame;
52648  ma_uint32 iChannelOut;
52649 
52650  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52651  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52652  ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52653  if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52654  pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
52655  } else {
52656  pFramesOut[iChannelOut] = 0;
52657  }
52658  }
52659 
52660  pFramesOut += channelsOut;
52661  pFramesIn += channelsIn;
52662  }
52663 }
52664 
52665 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)
52666 {
52667  ma_uint64 iFrame;
52668  ma_uint32 iChannelOut;
52669 
52670  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52671  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52672  ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52673  if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52674  pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
52675  } else {
52676  pFramesOut[iChannelOut] = 0;
52677  }
52678  }
52679 
52680  pFramesOut += channelsOut;
52681  pFramesIn += channelsIn;
52682  }
52683 }
52684 
52685 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)
52686 {
52687  if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) {
52688  return MA_INVALID_ARGS;
52689  }
52690 
52691  switch (format)
52692  {
52693  case ma_format_u8:
52694  {
52695  ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52696  } break;
52697 
52698  case ma_format_s16:
52699  {
52700  ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52701  } break;
52702 
52703  case ma_format_s24:
52704  {
52705  ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52706  } break;
52707 
52708  case ma_format_s32:
52709  {
52710  ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52711  } break;
52712 
52713  case ma_format_f32:
52714  {
52715  ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52716  } break;
52717 
52718  default: return MA_INVALID_ARGS; /* Unknown format. */
52719  }
52720 
52721  return MA_SUCCESS;
52722 }
52723 
52724 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)
52725 {
52726  ma_uint64 iFrame;
52727  ma_uint32 iChannelIn;
52728  ma_uint32 accumulationCount;
52729 
52730  if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) {
52731  return MA_INVALID_ARGS;
52732  }
52733 
52734  /* In this case the output stream needs to be the average of all channels, ignoring NONE. */
52735 
52736  /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */
52737  accumulationCount = 0;
52738  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
52739  if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) {
52740  accumulationCount += 1;
52741  }
52742  }
52743 
52744  if (accumulationCount > 0) { /* <-- Prevent a division by zero. */
52745  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52746  float accumulation = 0;
52747 
52748  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
52749  ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
52750  if (channelIn != MA_CHANNEL_NONE) {
52751  accumulation += pFramesIn[iChannelIn];
52752  }
52753  }
52754 
52755  pFramesOut[0] = accumulation / accumulationCount;
52756  pFramesOut += 1;
52757  pFramesIn += channelsIn;
52758  }
52759  } else {
52760  ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1);
52761  }
52762 
52763  return MA_SUCCESS;
52764 }
52765 
52766 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)
52767 {
52768  ma_uint64 iFrame;
52769  ma_uint32 iChannelOut;
52770 
52771  if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) {
52772  return MA_INVALID_ARGS;
52773  }
52774 
52775  /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */
52776  switch (monoExpansionMode)
52777  {
52778  case ma_mono_expansion_mode_average:
52779  {
52780  float weight;
52781  ma_uint32 validChannelCount = 0;
52782 
52783  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52784  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52785  if (channelOut != MA_CHANNEL_NONE) {
52786  validChannelCount += 1;
52787  }
52788  }
52789 
52790  weight = 1.0f / validChannelCount;
52791 
52792  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52793  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52794  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52795  if (channelOut != MA_CHANNEL_NONE) {
52796  pFramesOut[iChannelOut] = pFramesIn[0] * weight;
52797  }
52798  }
52799 
52800  pFramesOut += channelsOut;
52801  pFramesIn += 1;
52802  }
52803  } break;
52804 
52805  case ma_mono_expansion_mode_stereo_only:
52806  {
52807  if (channelsOut >= 2) {
52808  ma_uint32 iChannelLeft = (ma_uint32)-1;
52809  ma_uint32 iChannelRight = (ma_uint32)-1;
52810 
52811  /*
52812  We first need to find our stereo channels. We prefer front-left and front-right, but
52813  if they're not available, we'll also try side-left and side-right. If neither are
52814  available we'll fall through to the default case below.
52815  */
52816  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52817  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52818  if (channelOut == MA_CHANNEL_SIDE_LEFT) {
52819  iChannelLeft = iChannelOut;
52820  }
52821  if (channelOut == MA_CHANNEL_SIDE_RIGHT) {
52822  iChannelRight = iChannelOut;
52823  }
52824  }
52825 
52826  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52827  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52828  if (channelOut == MA_CHANNEL_FRONT_LEFT) {
52829  iChannelLeft = iChannelOut;
52830  }
52831  if (channelOut == MA_CHANNEL_FRONT_RIGHT) {
52832  iChannelRight = iChannelOut;
52833  }
52834  }
52835 
52836 
52837  if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) {
52838  /* We found our stereo channels so we can duplicate the signal across those channels. */
52839  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52840  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52841  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52842  if (channelOut != MA_CHANNEL_NONE) {
52843  if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) {
52844  pFramesOut[iChannelOut] = pFramesIn[0];
52845  } else {
52846  pFramesOut[iChannelOut] = 0.0f;
52847  }
52848  }
52849  }
52850 
52851  pFramesOut += channelsOut;
52852  pFramesIn += 1;
52853  }
52854 
52855  break; /* Get out of the switch. */
52856  } else {
52857  /* Fallthrough. Does not have left and right channels. */
52858  goto default_handler;
52859  }
52860  } else {
52861  /* Fallthrough. Does not have stereo channels. */
52862  goto default_handler;
52863  }
52864  }; /* Fallthrough. See comments above. */
52865 
52866  case ma_mono_expansion_mode_duplicate:
52867  default:
52868  {
52869  default_handler:
52870  {
52871  if (channelsOut <= MA_MAX_CHANNELS) {
52872  ma_bool32 hasEmptyChannel = MA_FALSE;
52873  ma_channel channelPositions[MA_MAX_CHANNELS];
52874  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52875  channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52876  if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) {
52877  hasEmptyChannel = MA_TRUE;
52878  }
52879  }
52880 
52881  if (hasEmptyChannel == MA_FALSE) {
52882  /*
52883  Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully
52884  help the compiler with auto-vectorization.m
52885  */
52886  if (channelsOut == 2) {
52887  #if defined(MA_SUPPORT_SSE2)
52888  if (ma_has_sse2()) {
52889  /* We want to do two frames in each iteration. */
52890  ma_uint64 unrolledFrameCount = frameCount >> 1;
52891 
52892  for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {
52893  __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);
52894  __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);
52895  _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0)));
52896  }
52897 
52898  /* Tail. */
52899  iFrame = unrolledFrameCount << 1;
52900  goto generic_on_fastpath;
52901  } else
52902  #endif
52903  {
52904  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52905  for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) {
52906  pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame];
52907  }
52908  }
52909  }
52910  } else if (channelsOut == 6) {
52911  #if defined(MA_SUPPORT_SSE2)
52912  if (ma_has_sse2()) {
52913  /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */
52914  ma_uint64 unrolledFrameCount = frameCount >> 1;
52915 
52916  for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {
52917  __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);
52918  __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);
52919 
52920  _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0);
52921  _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0)));
52922  _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1);
52923  }
52924 
52925  /* Tail. */
52926  iFrame = unrolledFrameCount << 1;
52927  goto generic_on_fastpath;
52928  } else
52929  #endif
52930  {
52931  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52932  for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) {
52933  pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame];
52934  }
52935  }
52936  }
52937  } else if (channelsOut == 8) {
52938  #if defined(MA_SUPPORT_SSE2)
52939  if (ma_has_sse2()) {
52940  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52941  __m128 in = _mm_set1_ps(pFramesIn[iFrame]);
52942  _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in);
52943  _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in);
52944  }
52945  } else
52946  #endif
52947  {
52948  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52949  for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) {
52950  pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame];
52951  }
52952  }
52953  }
52954  } else {
52955  iFrame = 0;
52956 
52957  #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */
52958  generic_on_fastpath:
52959  #endif
52960  {
52961  for (; iFrame < frameCount; iFrame += 1) {
52962  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52963  pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
52964  }
52965  }
52966  }
52967  }
52968  } else {
52969  /* Slow path. Need to handle MA_CHANNEL_NONE. */
52970  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52971  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52972  if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) {
52973  pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
52974  }
52975  }
52976  }
52977  }
52978  } else {
52979  /* Slow path. Too many channels to store on the stack. */
52980  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52981  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52982  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52983  if (channelOut != MA_CHANNEL_NONE) {
52984  pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
52985  }
52986  }
52987  }
52988  }
52989  }
52990  } break;
52991  }
52992 
52993  return MA_SUCCESS;
52994 }
52995 
52996 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)
52997 {
52998  ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode);
52999 
53000  /* Optimized Path: Passthrough */
53001  if (conversionPath == ma_channel_conversion_path_passthrough) {
53002  ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut);
53003  return;
53004  }
53005 
53006  /* Special Path: Mono Output. */
53007  if (conversionPath == ma_channel_conversion_path_mono_out) {
53008  ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount);
53009  return;
53010  }
53011 
53012  /* Special Path: Mono Input. */
53013  if (conversionPath == ma_channel_conversion_path_mono_in) {
53014  ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode);
53015  return;
53016  }
53017 
53018  /* Getting here means we aren't running on an optimized conversion path. */
53019  if (channelsOut <= MA_MAX_CHANNELS) {
53020  ma_result result;
53021 
53022  if (mode == ma_channel_mix_mode_simple) {
53023  ma_channel shuffleTable[MA_MAX_CHANNELS];
53024 
53025  result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable);
53026  if (result != MA_SUCCESS) {
53027  return;
53028  }
53029 
53030  result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32);
53031  if (result != MA_SUCCESS) {
53032  return;
53033  }
53034  } else {
53035  ma_uint32 iFrame;
53036  ma_uint32 iChannelOut;
53037  ma_uint32 iChannelIn;
53038  float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */
53039 
53040  /*
53041  If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to
53042  fall back to a slower path because otherwise we'll run out of stack space.
53043  */
53044  if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) {
53045  /* Pre-compute weights. */
53046  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
53047  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
53048  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53049  ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
53050  weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
53051  }
53052  }
53053 
53054  iFrame = 0;
53055 
53056  /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */
53057  if (channelsOut == 8) {
53058  /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */
53059  if (channelsIn == 2) {
53060  for (; iFrame < frameCount; iFrame += 1) {
53061  float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
53062 
53063  accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0];
53064  accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0];
53065  accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0];
53066  accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0];
53067  accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0];
53068  accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0];
53069  accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0];
53070  accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0];
53071 
53072  accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1];
53073  accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1];
53074  accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1];
53075  accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1];
53076  accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1];
53077  accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1];
53078  accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1];
53079  accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1];
53080 
53081  pFramesOut[iFrame*8 + 0] = accumulation[0];
53082  pFramesOut[iFrame*8 + 1] = accumulation[1];
53083  pFramesOut[iFrame*8 + 2] = accumulation[2];
53084  pFramesOut[iFrame*8 + 3] = accumulation[3];
53085  pFramesOut[iFrame*8 + 4] = accumulation[4];
53086  pFramesOut[iFrame*8 + 5] = accumulation[5];
53087  pFramesOut[iFrame*8 + 6] = accumulation[6];
53088  pFramesOut[iFrame*8 + 7] = accumulation[7];
53089  }
53090  } else {
53091  /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */
53092  for (; iFrame < frameCount; iFrame += 1) {
53093  float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
53094 
53095  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53096  accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];
53097  accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];
53098  accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];
53099  accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];
53100  accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];
53101  accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];
53102  accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn];
53103  accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn];
53104  }
53105 
53106  pFramesOut[iFrame*8 + 0] = accumulation[0];
53107  pFramesOut[iFrame*8 + 1] = accumulation[1];
53108  pFramesOut[iFrame*8 + 2] = accumulation[2];
53109  pFramesOut[iFrame*8 + 3] = accumulation[3];
53110  pFramesOut[iFrame*8 + 4] = accumulation[4];
53111  pFramesOut[iFrame*8 + 5] = accumulation[5];
53112  pFramesOut[iFrame*8 + 6] = accumulation[6];
53113  pFramesOut[iFrame*8 + 7] = accumulation[7];
53114  }
53115  }
53116  } else if (channelsOut == 6) {
53117  /*
53118  When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll
53119  expand our weights and do two frames at a time.
53120  */
53121  for (; iFrame < frameCount; iFrame += 1) {
53122  float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
53123 
53124  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53125  accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];
53126  accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];
53127  accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];
53128  accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];
53129  accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];
53130  accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];
53131  }
53132 
53133  pFramesOut[iFrame*6 + 0] = accumulation[0];
53134  pFramesOut[iFrame*6 + 1] = accumulation[1];
53135  pFramesOut[iFrame*6 + 2] = accumulation[2];
53136  pFramesOut[iFrame*6 + 3] = accumulation[3];
53137  pFramesOut[iFrame*6 + 4] = accumulation[4];
53138  pFramesOut[iFrame*6 + 5] = accumulation[5];
53139  }
53140  }
53141 
53142  /* Leftover frames. */
53143  for (; iFrame < frameCount; iFrame += 1) {
53144  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
53145  float accumulation = 0;
53146 
53147  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53148  accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn];
53149  }
53150 
53151  pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;
53152  }
53153  }
53154  } else {
53155  /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */
53156  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53157  for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
53158  float accumulation = 0;
53159  ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
53160 
53161  for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53162  ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
53163  accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
53164  }
53165 
53166  pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;
53167  }
53168  }
53169  }
53170  }
53171  } else {
53172  /* Fall back to silence. If you hit this, what are you doing with so many channels?! */
53173  ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut);
53174  }
53175 }
53176 
53177 
53178 typedef struct
53179 {
53180  size_t sizeInBytes;
53181  size_t channelMapInOffset;
53182  size_t channelMapOutOffset;
53183  size_t shuffleTableOffset;
53184  size_t weightsOffset;
53185 } ma_channel_converter_heap_layout;
53186 
53187 static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig)
53188 {
53189  return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode);
53190 }
53191 
53192 static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout)
53193 {
53194  ma_channel_conversion_path conversionPath;
53195 
53196  MA_ASSERT(pHeapLayout != NULL);
53197 
53198  if (pConfig == NULL) {
53199  return MA_INVALID_ARGS;
53200  }
53201 
53202  if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
53203  return MA_INVALID_ARGS;
53204  }
53205 
53206  if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) {
53207  return MA_INVALID_ARGS;
53208  }
53209 
53210  if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) {
53211  return MA_INVALID_ARGS;
53212  }
53213 
53214  pHeapLayout->sizeInBytes = 0;
53215 
53216  /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */
53217  pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
53218  if (pConfig->pChannelMapIn != NULL) {
53219  pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn;
53220  }
53221 
53222  /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */
53223  pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
53224  if (pConfig->pChannelMapOut != NULL) {
53225  pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut;
53226  }
53227 
53228  /* Alignment for the next section. */
53229  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
53230 
53231  /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */
53232  conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
53233 
53234  /* Shuffle table */
53235  pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes;
53236  if (conversionPath == ma_channel_conversion_path_shuffle) {
53237  pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut;
53238  }
53239 
53240  /* Weights */
53241  pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes;
53242  if (conversionPath == ma_channel_conversion_path_weights) {
53243  pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn;
53244  pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut;
53245  }
53246 
53247  /* Make sure allocation size is aligned. */
53248  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
53249 
53250  return MA_SUCCESS;
53251 }
53252 
53253 MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes)
53254 {
53255  ma_result result;
53256  ma_channel_converter_heap_layout heapLayout;
53257 
53258  if (pHeapSizeInBytes == NULL) {
53259  return MA_INVALID_ARGS;
53260  }
53261 
53262  *pHeapSizeInBytes = 0;
53263 
53264  result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
53265  if (result != MA_SUCCESS) {
53266  return result;
53267  }
53268 
53269  *pHeapSizeInBytes = heapLayout.sizeInBytes;
53270 
53271  return MA_SUCCESS;
53272 }
53273 
53274 MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter)
53275 {
53276  ma_result result;
53277  ma_channel_converter_heap_layout heapLayout;
53278 
53279  if (pConverter == NULL) {
53280  return MA_INVALID_ARGS;
53281  }
53282 
53283  MA_ZERO_OBJECT(pConverter);
53284 
53285  result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
53286  if (result != MA_SUCCESS) {
53287  return result;
53288  }
53289 
53290  pConverter->_pHeap = pHeap;
53291  MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes);
53292 
53293  pConverter->format = pConfig->format;
53294  pConverter->channelsIn = pConfig->channelsIn;
53295  pConverter->channelsOut = pConfig->channelsOut;
53296  pConverter->mixingMode = pConfig->mixingMode;
53297 
53298  if (pConfig->pChannelMapIn != NULL) {
53299  pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
53300  ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn);
53301  } else {
53302  pConverter->pChannelMapIn = NULL; /* Use default channel map. */
53303  }
53304 
53305  if (pConfig->pChannelMapOut != NULL) {
53306  pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
53307  ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
53308  } else {
53309  pConverter->pChannelMapOut = NULL; /* Use default channel map. */
53310  }
53311 
53312  pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
53313 
53314  if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) {
53315  pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset);
53316  ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable);
53317  }
53318 
53319  if (pConverter->conversionPath == ma_channel_conversion_path_weights) {
53320  ma_uint32 iChannelIn;
53321  ma_uint32 iChannelOut;
53322 
53323  if (pConverter->format == ma_format_f32) {
53324  pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset);
53325  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
53326  pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn)));
53327  }
53328  } else {
53329  pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset);
53330  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
53331  pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn)));
53332  }
53333  }
53334 
53335  /* Silence our weights by default. */
53336  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
53337  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
53338  if (pConverter->format == ma_format_f32) {
53339  pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f;
53340  } else {
53341  pConverter->weights.s16[iChannelIn][iChannelOut] = 0;
53342  }
53343  }
53344  }
53345 
53346  /*
53347  We now need to fill out our weights table. This is determined by the mixing mode.
53348  */
53349 
53350  /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
53351  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53352  ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
53353 
53354  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53355  ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
53356 
53357  if (channelPosIn == channelPosOut) {
53358  float weight = 1;
53359 
53360  if (pConverter->format == ma_format_f32) {
53361  pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
53362  } else {
53363  pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
53364  }
53365  }
53366  }
53367  }
53368 
53369  switch (pConverter->mixingMode)
53370  {
53371  case ma_channel_mix_mode_custom_weights:
53372  {
53373  if (pConfig->ppWeights == NULL) {
53374  return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */
53375  }
53376 
53377  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
53378  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
53379  float weight = pConfig->ppWeights[iChannelIn][iChannelOut];
53380 
53381  if (pConverter->format == ma_format_f32) {
53382  pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
53383  } else {
53384  pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
53385  }
53386  }
53387  }
53388  } break;
53389 
53390  case ma_channel_mix_mode_simple:
53391  {
53392  /*
53393  In simple mode, only set weights for channels that have exactly matching types, leave the rest at
53394  zero. The 1:1 mappings have already been covered before this switch statement.
53395  */
53396  } break;
53397 
53398  case ma_channel_mix_mode_rectangular:
53399  default:
53400  {
53401  /* Unmapped input channels. */
53402  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53403  ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
53404 
53405  if (ma_is_spatial_channel_position(channelPosIn)) {
53406  if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) {
53407  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53408  ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
53409 
53410  if (ma_is_spatial_channel_position(channelPosOut)) {
53411  float weight = 0;
53412  if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
53413  weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
53414  }
53415 
53416  /* Only apply the weight if we haven't already got some contribution from the respective channels. */
53417  if (pConverter->format == ma_format_f32) {
53418  if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
53419  pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
53420  }
53421  } else {
53422  if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
53423  pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
53424  }
53425  }
53426  }
53427  }
53428  }
53429  }
53430  }
53431 
53432  /* Unmapped output channels. */
53433  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53434  ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
53435 
53436  if (ma_is_spatial_channel_position(channelPosOut)) {
53437  if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) {
53438  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53439  ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
53440 
53441  if (ma_is_spatial_channel_position(channelPosIn)) {
53442  float weight = 0;
53443  if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
53444  weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
53445  }
53446 
53447  /* Only apply the weight if we haven't already got some contribution from the respective channels. */
53448  if (pConverter->format == ma_format_f32) {
53449  if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
53450  pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
53451  }
53452  } else {
53453  if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
53454  pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
53455  }
53456  }
53457  }
53458  }
53459  }
53460  }
53461  }
53462 
53463  /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */
53464  if (pConfig->calculateLFEFromSpatialChannels) {
53465  if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) {
53466  ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn);
53467  ma_uint32 iChannelOutLFE;
53468 
53469  if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) {
53470  const float weightForLFE = 1.0f / spatialChannelCount;
53471  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53472  const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
53473  if (ma_is_spatial_channel_position(channelPosIn)) {
53474  if (pConverter->format == ma_format_f32) {
53475  if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) {
53476  pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE;
53477  }
53478  } else {
53479  if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) {
53480  pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE);
53481  }
53482  }
53483  }
53484  }
53485  }
53486  }
53487  }
53488  } break;
53489  }
53490  }
53491 
53492  return MA_SUCCESS;
53493 }
53494 
53495 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter)
53496 {
53497  ma_result result;
53498  size_t heapSizeInBytes;
53499  void* pHeap;
53500 
53501  result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes);
53502  if (result != MA_SUCCESS) {
53503  return result;
53504  }
53505 
53506  if (heapSizeInBytes > 0) {
53507  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
53508  if (pHeap == NULL) {
53509  return MA_OUT_OF_MEMORY;
53510  }
53511  } else {
53512  pHeap = NULL;
53513  }
53514 
53515  result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter);
53516  if (result != MA_SUCCESS) {
53517  ma_free(pHeap, pAllocationCallbacks);
53518  return result;
53519  }
53520 
53521  pConverter->_ownsHeap = MA_TRUE;
53522  return MA_SUCCESS;
53523 }
53524 
53525 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
53526 {
53527  if (pConverter == NULL) {
53528  return;
53529  }
53530 
53531  if (pConverter->_ownsHeap) {
53532  ma_free(pConverter->_pHeap, pAllocationCallbacks);
53533  }
53534 }
53535 
53536 static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53537 {
53538  MA_ASSERT(pConverter != NULL);
53539  MA_ASSERT(pFramesOut != NULL);
53540  MA_ASSERT(pFramesIn != NULL);
53541 
53542  ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
53543  return MA_SUCCESS;
53544 }
53545 
53546 static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53547 {
53548  MA_ASSERT(pConverter != NULL);
53549  MA_ASSERT(pFramesOut != NULL);
53550  MA_ASSERT(pFramesIn != NULL);
53551  MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
53552 
53553  return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format);
53554 }
53555 
53556 static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53557 {
53558  ma_uint64 iFrame;
53559 
53560  MA_ASSERT(pConverter != NULL);
53561  MA_ASSERT(pFramesOut != NULL);
53562  MA_ASSERT(pFramesIn != NULL);
53563  MA_ASSERT(pConverter->channelsIn == 1);
53564 
53565  switch (pConverter->format)
53566  {
53567  case ma_format_u8:
53568  {
53569  /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
53570  const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
53571 
53572  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53573  ma_uint32 iChannel;
53574  for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53575  pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame];
53576  }
53577  }
53578  } break;
53579 
53580  case ma_format_s16:
53581  {
53582  /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
53583  const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
53584 
53585  if (pConverter->channelsOut == 2) {
53586  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53587  pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
53588  pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
53589  }
53590  } else {
53591  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53592  ma_uint32 iChannel;
53593  for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53594  pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
53595  }
53596  }
53597  }
53598  } break;
53599 
53600  case ma_format_s24:
53601  {
53602  /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
53603  const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
53604 
53605  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53606  ma_uint32 iChannel;
53607  for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53608  ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel;
53609  ma_uint64 iSampleIn = iFrame;
53610  pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0];
53611  pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1];
53612  pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2];
53613  }
53614  }
53615  } break;
53616 
53617  case ma_format_s32:
53618  {
53619  /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
53620  const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
53621 
53622  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53623  ma_uint32 iChannel;
53624  for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53625  pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame];
53626  }
53627  }
53628  } break;
53629 
53630  case ma_format_f32:
53631  {
53632  /* */ float* pFramesOutF32 = ( float*)pFramesOut;
53633  const float* pFramesInF32 = (const float*)pFramesIn;
53634 
53635  if (pConverter->channelsOut == 2) {
53636  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53637  pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
53638  pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
53639  }
53640  } else {
53641  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53642  ma_uint32 iChannel;
53643  for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53644  pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
53645  }
53646  }
53647  }
53648  } break;
53649 
53650  default: return MA_INVALID_OPERATION; /* Unknown format. */
53651  }
53652 
53653  return MA_SUCCESS;
53654 }
53655 
53656 static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53657 {
53658  ma_uint64 iFrame;
53659  ma_uint32 iChannel;
53660 
53661  MA_ASSERT(pConverter != NULL);
53662  MA_ASSERT(pFramesOut != NULL);
53663  MA_ASSERT(pFramesIn != NULL);
53664  MA_ASSERT(pConverter->channelsOut == 1);
53665 
53666  switch (pConverter->format)
53667  {
53668  case ma_format_u8:
53669  {
53670  /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
53671  const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
53672 
53673  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53674  ma_int32 t = 0;
53675  for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53676  t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]);
53677  }
53678 
53679  pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut);
53680  }
53681  } break;
53682 
53683  case ma_format_s16:
53684  {
53685  /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
53686  const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
53687 
53688  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53689  ma_int32 t = 0;
53690  for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53691  t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel];
53692  }
53693 
53694  pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn);
53695  }
53696  } break;
53697 
53698  case ma_format_s24:
53699  {
53700  /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
53701  const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
53702 
53703  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53704  ma_int64 t = 0;
53705  for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53706  t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]);
53707  }
53708 
53709  ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]);
53710  }
53711  } break;
53712 
53713  case ma_format_s32:
53714  {
53715  /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
53716  const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
53717 
53718  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53719  ma_int64 t = 0;
53720  for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53721  t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel];
53722  }
53723 
53724  pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn);
53725  }
53726  } break;
53727 
53728  case ma_format_f32:
53729  {
53730  /* */ float* pFramesOutF32 = ( float*)pFramesOut;
53731  const float* pFramesInF32 = (const float*)pFramesIn;
53732 
53733  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53734  float t = 0;
53735  for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53736  t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel];
53737  }
53738 
53739  pFramesOutF32[iFrame] = t / pConverter->channelsIn;
53740  }
53741  } break;
53742 
53743  default: return MA_INVALID_OPERATION; /* Unknown format. */
53744  }
53745 
53746  return MA_SUCCESS;
53747 }
53748 
53749 static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53750 {
53751  ma_uint32 iFrame;
53752  ma_uint32 iChannelIn;
53753  ma_uint32 iChannelOut;
53754 
53755  MA_ASSERT(pConverter != NULL);
53756  MA_ASSERT(pFramesOut != NULL);
53757  MA_ASSERT(pFramesIn != NULL);
53758 
53759  /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
53760 
53761  /* Clear. */
53762  ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
53763 
53764  /* Accumulate. */
53765  switch (pConverter->format)
53766  {
53767  case ma_format_u8:
53768  {
53769  /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
53770  const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
53771 
53772  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53773  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53774  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53775  ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]);
53776  ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]);
53777  ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127);
53778  pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s);
53779  }
53780  }
53781  }
53782  } break;
53783 
53784  case ma_format_s16:
53785  {
53786  /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
53787  const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
53788 
53789  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53790  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53791  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53792  ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
53793  s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
53794 
53795  pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
53796  }
53797  }
53798  }
53799  } break;
53800 
53801  case ma_format_s24:
53802  {
53803  /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
53804  const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
53805 
53806  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53807  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53808  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53809  ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
53810  ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]);
53811  ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607);
53812  ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
53813  }
53814  }
53815  }
53816  } break;
53817 
53818  case ma_format_s32:
53819  {
53820  /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
53821  const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
53822 
53823  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53824  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53825  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53826  ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];
53827  s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
53828 
53829  pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);
53830  }
53831  }
53832  }
53833  } break;
53834 
53835  case ma_format_f32:
53836  {
53837  /* */ float* pFramesOutF32 = ( float*)pFramesOut;
53838  const float* pFramesInF32 = (const float*)pFramesIn;
53839 
53840  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53841  for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53842  for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53843  pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
53844  }
53845  }
53846  }
53847  } break;
53848 
53849  default: return MA_INVALID_OPERATION; /* Unknown format. */
53850  }
53851 
53852  return MA_SUCCESS;
53853 }
53854 
53855 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53856 {
53857  if (pConverter == NULL) {
53858  return MA_INVALID_ARGS;
53859  }
53860 
53861  if (pFramesOut == NULL) {
53862  return MA_INVALID_ARGS;
53863  }
53864 
53865  if (pFramesIn == NULL) {
53866  ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
53867  return MA_SUCCESS;
53868  }
53869 
53870  switch (pConverter->conversionPath)
53871  {
53872  case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
53873  case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount);
53874  case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount);
53875  case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
53876  case ma_channel_conversion_path_weights:
53877  default:
53878  {
53879  return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
53880  }
53881  }
53882 }
53883 
53884 MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
53885 {
53886  if (pConverter == NULL || pChannelMap == NULL) {
53887  return MA_INVALID_ARGS;
53888  }
53889 
53890  ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn);
53891 
53892  return MA_SUCCESS;
53893 }
53894 
53895 MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
53896 {
53897  if (pConverter == NULL || pChannelMap == NULL) {
53898  return MA_INVALID_ARGS;
53899  }
53900 
53901  ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut);
53902 
53903  return MA_SUCCESS;
53904 }
53905 
53906 
53907 /**************************************************************************************************************************************************************
53908 
53909 Data Conversion
53910 
53911 **************************************************************************************************************************************************************/
53912 MA_API ma_data_converter_config ma_data_converter_config_init_default(void)
53913 {
53914  ma_data_converter_config config;
53915  MA_ZERO_OBJECT(&config);
53916 
53917  config.ditherMode = ma_dither_mode_none;
53918  config.resampling.algorithm = ma_resample_algorithm_linear;
53919  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. */
53920 
53921  /* Linear resampling defaults. */
53922  config.resampling.linear.lpfOrder = 1;
53923 
53924  return config;
53925 }
53926 
53927 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)
53928 {
53929  ma_data_converter_config config = ma_data_converter_config_init_default();
53930  config.formatIn = formatIn;
53931  config.formatOut = formatOut;
53932  config.channelsIn = channelsIn;
53933  config.channelsOut = channelsOut;
53934  config.sampleRateIn = sampleRateIn;
53935  config.sampleRateOut = sampleRateOut;
53936 
53937  return config;
53938 }
53939 
53940 
53941 typedef struct
53942 {
53943  size_t sizeInBytes;
53944  size_t channelConverterOffset;
53945  size_t resamplerOffset;
53946 } ma_data_converter_heap_layout;
53947 
53948 static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig)
53949 {
53950  MA_ASSERT(pConfig != NULL);
53951 
53952  return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut;
53953 }
53954 
53955 static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig)
53956 {
53957  MA_ASSERT(pConfig != NULL);
53958 
53959  /*
53960  We want to avoid as much data conversion as possible. The channel converter and linear
53961  resampler both support s16 and f32 natively. We need to decide on the format to use for this
53962  stage. We call this the mid format because it's used in the middle stage of the conversion
53963  pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it
53964  will do the same thing for the input format. If it's neither we just use f32. If we are using a
53965  custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced
53966  to use that if resampling is required.
53967  */
53968  if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) {
53969  return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */
53970  } else {
53971  /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) {
53972  return pConfig->formatOut;
53973  } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) {
53974  return pConfig->formatIn;
53975  } else {
53976  return ma_format_f32;
53977  }
53978  }
53979 }
53980 
53981 static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
53982 {
53983  ma_channel_converter_config channelConverterConfig;
53984 
53985  MA_ASSERT(pConfig != NULL);
53986 
53987  channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode);
53988  channelConverterConfig.ppWeights = pConfig->ppChannelWeights;
53989  channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels;
53990 
53991  return channelConverterConfig;
53992 }
53993 
53994 static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
53995 {
53996  ma_resampler_config resamplerConfig;
53997  ma_uint32 resamplerChannels;
53998 
53999  MA_ASSERT(pConfig != NULL);
54000 
54001  /* 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. */
54002  if (pConfig->channelsIn < pConfig->channelsOut) {
54003  resamplerChannels = pConfig->channelsIn;
54004  } else {
54005  resamplerChannels = pConfig->channelsOut;
54006  }
54007 
54008  resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm);
54009  resamplerConfig.linear = pConfig->resampling.linear;
54010  resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable;
54011  resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData;
54012 
54013  return resamplerConfig;
54014 }
54015 
54016 static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout)
54017 {
54018  ma_result result;
54019 
54020  MA_ASSERT(pHeapLayout != NULL);
54021 
54022  MA_ZERO_OBJECT(pHeapLayout);
54023 
54024  if (pConfig == NULL) {
54025  return MA_INVALID_ARGS;
54026  }
54027 
54028  if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
54029  return MA_INVALID_ARGS;
54030  }
54031 
54032  pHeapLayout->sizeInBytes = 0;
54033 
54034  /* Channel converter. */
54035  pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes;
54036  {
54037  size_t heapSizeInBytes;
54038  ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
54039 
54040  result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes);
54041  if (result != MA_SUCCESS) {
54042  return result;
54043  }
54044 
54045  pHeapLayout->sizeInBytes += heapSizeInBytes;
54046  }
54047 
54048  /* Resampler. */
54049  pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
54050  if (ma_data_converter_config_is_resampler_required(pConfig)) {
54051  size_t heapSizeInBytes;
54052  ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
54053 
54054  result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes);
54055  if (result != MA_SUCCESS) {
54056  return result;
54057  }
54058 
54059  pHeapLayout->sizeInBytes += heapSizeInBytes;
54060  }
54061 
54062  /* Make sure allocation size is aligned. */
54063  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
54064 
54065  return MA_SUCCESS;
54066 }
54067 
54068 MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes)
54069 {
54070  ma_result result;
54071  ma_data_converter_heap_layout heapLayout;
54072 
54073  if (pHeapSizeInBytes == NULL) {
54074  return MA_INVALID_ARGS;
54075  }
54076 
54077  *pHeapSizeInBytes = 0;
54078 
54079  result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
54080  if (result != MA_SUCCESS) {
54081  return result;
54082  }
54083 
54084  *pHeapSizeInBytes = heapLayout.sizeInBytes;
54085 
54086  return MA_SUCCESS;
54087 }
54088 
54089 MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter)
54090 {
54091  ma_result result;
54092  ma_data_converter_heap_layout heapLayout;
54093  ma_format midFormat;
54094  ma_bool32 isResamplingRequired;
54095 
54096  if (pConverter == NULL) {
54097  return MA_INVALID_ARGS;
54098  }
54099 
54100  MA_ZERO_OBJECT(pConverter);
54101 
54102  result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
54103  if (result != MA_SUCCESS) {
54104  return result;
54105  }
54106 
54107  pConverter->_pHeap = pHeap;
54108  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
54109 
54110  pConverter->formatIn = pConfig->formatIn;
54111  pConverter->formatOut = pConfig->formatOut;
54112  pConverter->channelsIn = pConfig->channelsIn;
54113  pConverter->channelsOut = pConfig->channelsOut;
54114  pConverter->sampleRateIn = pConfig->sampleRateIn;
54115  pConverter->sampleRateOut = pConfig->sampleRateOut;
54116  pConverter->ditherMode = pConfig->ditherMode;
54117 
54118  /*
54119  Determine if resampling is required. We need to do this so we can determine an appropriate
54120  mid format to use. If resampling is required, the mid format must be ma_format_f32 since
54121  that is the only one that is guaranteed to supported by custom resampling backends.
54122  */
54123  isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig);
54124  midFormat = ma_data_converter_config_get_mid_format(pConfig);
54125 
54126 
54127  /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
54128  {
54129  ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
54130 
54131  result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter);
54132  if (result != MA_SUCCESS) {
54133  return result;
54134  }
54135 
54136  /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */
54137  if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) {
54138  pConverter->hasChannelConverter = MA_TRUE;
54139  }
54140  }
54141 
54142 
54143  /* Resampler. */
54144  if (isResamplingRequired) {
54145  ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
54146 
54147  result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler);
54148  if (result != MA_SUCCESS) {
54149  return result;
54150  }
54151 
54152  pConverter->hasResampler = MA_TRUE;
54153  }
54154 
54155 
54156  /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */
54157  if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {
54158  /* 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. */
54159  if (pConverter->formatIn == pConverter->formatOut) {
54160  /* The formats are the same so we can just pass through. */
54161  pConverter->hasPreFormatConversion = MA_FALSE;
54162  pConverter->hasPostFormatConversion = MA_FALSE;
54163  } else {
54164  /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */
54165  pConverter->hasPreFormatConversion = MA_FALSE;
54166  pConverter->hasPostFormatConversion = MA_TRUE;
54167  }
54168  } else {
54169  /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */
54170  if (pConverter->formatIn != midFormat) {
54171  pConverter->hasPreFormatConversion = MA_TRUE;
54172  }
54173  if (pConverter->formatOut != midFormat) {
54174  pConverter->hasPostFormatConversion = MA_TRUE;
54175  }
54176  }
54177 
54178  /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
54179  if (pConverter->hasPreFormatConversion == MA_FALSE &&
54180  pConverter->hasPostFormatConversion == MA_FALSE &&
54181  pConverter->hasChannelConverter == MA_FALSE &&
54182  pConverter->hasResampler == MA_FALSE) {
54183  pConverter->isPassthrough = MA_TRUE;
54184  }
54185 
54186 
54187  /* We now need to determine our execution path. */
54188  if (pConverter->isPassthrough) {
54189  pConverter->executionPath = ma_data_converter_execution_path_passthrough;
54190  } else {
54191  if (pConverter->channelsIn < pConverter->channelsOut) {
54192  /* Do resampling first, if necessary. */
54193  MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
54194 
54195  if (pConverter->hasResampler) {
54196  pConverter->executionPath = ma_data_converter_execution_path_resample_first;
54197  } else {
54198  pConverter->executionPath = ma_data_converter_execution_path_channels_only;
54199  }
54200  } else {
54201  /* Do channel conversion first, if necessary. */
54202  if (pConverter->hasChannelConverter) {
54203  if (pConverter->hasResampler) {
54204  pConverter->executionPath = ma_data_converter_execution_path_channels_first;
54205  } else {
54206  pConverter->executionPath = ma_data_converter_execution_path_channels_only;
54207  }
54208  } else {
54209  /* Channel routing not required. */
54210  if (pConverter->hasResampler) {
54211  pConverter->executionPath = ma_data_converter_execution_path_resample_only;
54212  } else {
54213  pConverter->executionPath = ma_data_converter_execution_path_format_only;
54214  }
54215  }
54216  }
54217  }
54218 
54219  return MA_SUCCESS;
54220 }
54221 
54222 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter)
54223 {
54224  ma_result result;
54225  size_t heapSizeInBytes;
54226  void* pHeap;
54227 
54228  result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes);
54229  if (result != MA_SUCCESS) {
54230  return result;
54231  }
54232 
54233  if (heapSizeInBytes > 0) {
54234  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
54235  if (pHeap == NULL) {
54236  return MA_OUT_OF_MEMORY;
54237  }
54238  } else {
54239  pHeap = NULL;
54240  }
54241 
54242  result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter);
54243  if (result != MA_SUCCESS) {
54244  ma_free(pHeap, pAllocationCallbacks);
54245  return result;
54246  }
54247 
54248  pConverter->_ownsHeap = MA_TRUE;
54249  return MA_SUCCESS;
54250 }
54251 
54252 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
54253 {
54254  if (pConverter == NULL) {
54255  return;
54256  }
54257 
54258  if (pConverter->hasResampler) {
54259  ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks);
54260  }
54261 
54262  ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks);
54263 
54264  if (pConverter->_ownsHeap) {
54265  ma_free(pConverter->_pHeap, pAllocationCallbacks);
54266  }
54267 }
54268 
54269 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)
54270 {
54271  ma_uint64 frameCountIn;
54272  ma_uint64 frameCountOut;
54273  ma_uint64 frameCount;
54274 
54275  MA_ASSERT(pConverter != NULL);
54276 
54277  frameCountIn = 0;
54278  if (pFrameCountIn != NULL) {
54279  frameCountIn = *pFrameCountIn;
54280  }
54281 
54282  frameCountOut = 0;
54283  if (pFrameCountOut != NULL) {
54284  frameCountOut = *pFrameCountOut;
54285  }
54286 
54287  frameCount = ma_min(frameCountIn, frameCountOut);
54288 
54289  if (pFramesOut != NULL) {
54290  if (pFramesIn != NULL) {
54291  ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54292  } else {
54293  ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54294  }
54295  }
54296 
54297  if (pFrameCountIn != NULL) {
54298  *pFrameCountIn = frameCount;
54299  }
54300  if (pFrameCountOut != NULL) {
54301  *pFrameCountOut = frameCount;
54302  }
54303 
54304  return MA_SUCCESS;
54305 }
54306 
54307 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)
54308 {
54309  ma_uint64 frameCountIn;
54310  ma_uint64 frameCountOut;
54311  ma_uint64 frameCount;
54312 
54313  MA_ASSERT(pConverter != NULL);
54314 
54315  frameCountIn = 0;
54316  if (pFrameCountIn != NULL) {
54317  frameCountIn = *pFrameCountIn;
54318  }
54319 
54320  frameCountOut = 0;
54321  if (pFrameCountOut != NULL) {
54322  frameCountOut = *pFrameCountOut;
54323  }
54324 
54325  frameCount = ma_min(frameCountIn, frameCountOut);
54326 
54327  if (pFramesOut != NULL) {
54328  if (pFramesIn != NULL) {
54329  ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode);
54330  } else {
54331  ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54332  }
54333  }
54334 
54335  if (pFrameCountIn != NULL) {
54336  *pFrameCountIn = frameCount;
54337  }
54338  if (pFrameCountOut != NULL) {
54339  *pFrameCountOut = frameCount;
54340  }
54341 
54342  return MA_SUCCESS;
54343 }
54344 
54345 
54346 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)
54347 {
54348  ma_result result = MA_SUCCESS;
54349  ma_uint64 frameCountIn;
54350  ma_uint64 frameCountOut;
54351  ma_uint64 framesProcessedIn;
54352  ma_uint64 framesProcessedOut;
54353 
54354  MA_ASSERT(pConverter != NULL);
54355 
54356  frameCountIn = 0;
54357  if (pFrameCountIn != NULL) {
54358  frameCountIn = *pFrameCountIn;
54359  }
54360 
54361  frameCountOut = 0;
54362  if (pFrameCountOut != NULL) {
54363  frameCountOut = *pFrameCountOut;
54364  }
54365 
54366  framesProcessedIn = 0;
54367  framesProcessedOut = 0;
54368 
54369  while (framesProcessedOut < frameCountOut) {
54370  ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
54371  const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54372  const void* pFramesInThisIteration;
54373  /* */ void* pFramesOutThisIteration;
54374  ma_uint64 frameCountInThisIteration;
54375  ma_uint64 frameCountOutThisIteration;
54376 
54377  if (pFramesIn != NULL) {
54378  pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
54379  } else {
54380  pFramesInThisIteration = NULL;
54381  }
54382 
54383  if (pFramesOut != NULL) {
54384  pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54385  } else {
54386  pFramesOutThisIteration = NULL;
54387  }
54388 
54389  /* Do a pre format conversion if necessary. */
54390  if (pConverter->hasPreFormatConversion) {
54391  ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
54392  const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54393 
54394  frameCountInThisIteration = (frameCountIn - framesProcessedIn);
54395  if (frameCountInThisIteration > tempBufferInCap) {
54396  frameCountInThisIteration = tempBufferInCap;
54397  }
54398 
54399  if (pConverter->hasPostFormatConversion) {
54400  if (frameCountInThisIteration > tempBufferOutCap) {
54401  frameCountInThisIteration = tempBufferOutCap;
54402  }
54403  }
54404 
54405  if (pFramesInThisIteration != NULL) {
54406  ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
54407  } else {
54408  MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
54409  }
54410 
54411  frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
54412 
54413  if (pConverter->hasPostFormatConversion) {
54414  /* Both input and output conversion required. Output to the temp buffer. */
54415  if (frameCountOutThisIteration > tempBufferOutCap) {
54416  frameCountOutThisIteration = tempBufferOutCap;
54417  }
54418 
54419  result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
54420  } else {
54421  /* Only pre-format required. Output straight to the output buffer. */
54422  result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
54423  }
54424 
54425  if (result != MA_SUCCESS) {
54426  break;
54427  }
54428  } else {
54429  /* No pre-format required. Just read straight from the input buffer. */
54430  MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
54431 
54432  frameCountInThisIteration = (frameCountIn - framesProcessedIn);
54433  frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
54434  if (frameCountOutThisIteration > tempBufferOutCap) {
54435  frameCountOutThisIteration = tempBufferOutCap;
54436  }
54437 
54438  result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
54439  if (result != MA_SUCCESS) {
54440  break;
54441  }
54442  }
54443 
54444  /* If we are doing a post format conversion we need to do that now. */
54445  if (pConverter->hasPostFormatConversion) {
54446  if (pFramesOutThisIteration != NULL) {
54447  ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode);
54448  }
54449  }
54450 
54451  framesProcessedIn += frameCountInThisIteration;
54452  framesProcessedOut += frameCountOutThisIteration;
54453 
54454  MA_ASSERT(framesProcessedIn <= frameCountIn);
54455  MA_ASSERT(framesProcessedOut <= frameCountOut);
54456 
54457  if (frameCountOutThisIteration == 0) {
54458  break; /* Consumed all of our input data. */
54459  }
54460  }
54461 
54462  if (pFrameCountIn != NULL) {
54463  *pFrameCountIn = framesProcessedIn;
54464  }
54465  if (pFrameCountOut != NULL) {
54466  *pFrameCountOut = framesProcessedOut;
54467  }
54468 
54469  return result;
54470 }
54471 
54472 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)
54473 {
54474  MA_ASSERT(pConverter != NULL);
54475 
54476  if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
54477  /* Neither pre- nor post-format required. This is simple case where only resampling is required. */
54478  return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54479  } else {
54480  /* Format conversion required. */
54481  return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54482  }
54483 }
54484 
54485 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)
54486 {
54487  ma_result result;
54488  ma_uint64 frameCountIn;
54489  ma_uint64 frameCountOut;
54490  ma_uint64 frameCount;
54491 
54492  MA_ASSERT(pConverter != NULL);
54493 
54494  frameCountIn = 0;
54495  if (pFrameCountIn != NULL) {
54496  frameCountIn = *pFrameCountIn;
54497  }
54498 
54499  frameCountOut = 0;
54500  if (pFrameCountOut != NULL) {
54501  frameCountOut = *pFrameCountOut;
54502  }
54503 
54504  frameCount = ma_min(frameCountIn, frameCountOut);
54505 
54506  if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
54507  /* No format conversion required. */
54508  result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
54509  if (result != MA_SUCCESS) {
54510  return result;
54511  }
54512  } else {
54513  /* Format conversion required. */
54514  ma_uint64 framesProcessed = 0;
54515 
54516  while (framesProcessed < frameCount) {
54517  ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
54518  const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
54519  const void* pFramesInThisIteration;
54520  /* */ void* pFramesOutThisIteration;
54521  ma_uint64 frameCountThisIteration;
54522 
54523  if (pFramesIn != NULL) {
54524  pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
54525  } else {
54526  pFramesInThisIteration = NULL;
54527  }
54528 
54529  if (pFramesOut != NULL) {
54530  pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54531  } else {
54532  pFramesOutThisIteration = NULL;
54533  }
54534 
54535  /* Do a pre format conversion if necessary. */
54536  if (pConverter->hasPreFormatConversion) {
54537  ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
54538  const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
54539 
54540  frameCountThisIteration = (frameCount - framesProcessed);
54541  if (frameCountThisIteration > tempBufferInCap) {
54542  frameCountThisIteration = tempBufferInCap;
54543  }
54544 
54545  if (pConverter->hasPostFormatConversion) {
54546  if (frameCountThisIteration > tempBufferOutCap) {
54547  frameCountThisIteration = tempBufferOutCap;
54548  }
54549  }
54550 
54551  if (pFramesInThisIteration != NULL) {
54552  ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode);
54553  } else {
54554  MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
54555  }
54556 
54557  if (pConverter->hasPostFormatConversion) {
54558  /* Both input and output conversion required. Output to the temp buffer. */
54559  result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
54560  } else {
54561  /* Only pre-format required. Output straight to the output buffer. */
54562  result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
54563  }
54564 
54565  if (result != MA_SUCCESS) {
54566  break;
54567  }
54568  } else {
54569  /* No pre-format required. Just read straight from the input buffer. */
54570  MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
54571 
54572  frameCountThisIteration = (frameCount - framesProcessed);
54573  if (frameCountThisIteration > tempBufferOutCap) {
54574  frameCountThisIteration = tempBufferOutCap;
54575  }
54576 
54577  result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
54578  if (result != MA_SUCCESS) {
54579  break;
54580  }
54581  }
54582 
54583  /* If we are doing a post format conversion we need to do that now. */
54584  if (pConverter->hasPostFormatConversion) {
54585  if (pFramesOutThisIteration != NULL) {
54586  ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
54587  }
54588  }
54589 
54590  framesProcessed += frameCountThisIteration;
54591  }
54592  }
54593 
54594  if (pFrameCountIn != NULL) {
54595  *pFrameCountIn = frameCount;
54596  }
54597  if (pFrameCountOut != NULL) {
54598  *pFrameCountOut = frameCount;
54599  }
54600 
54601  return MA_SUCCESS;
54602 }
54603 
54604 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)
54605 {
54606  ma_result result;
54607  ma_uint64 frameCountIn;
54608  ma_uint64 frameCountOut;
54609  ma_uint64 framesProcessedIn;
54610  ma_uint64 framesProcessedOut;
54611  ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
54612  ma_uint64 tempBufferInCap;
54613  ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
54614  ma_uint64 tempBufferMidCap;
54615  ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
54616  ma_uint64 tempBufferOutCap;
54617 
54618  MA_ASSERT(pConverter != NULL);
54619  MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
54620  MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn);
54621  MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut);
54622 
54623  frameCountIn = 0;
54624  if (pFrameCountIn != NULL) {
54625  frameCountIn = *pFrameCountIn;
54626  }
54627 
54628  frameCountOut = 0;
54629  if (pFrameCountOut != NULL) {
54630  frameCountOut = *pFrameCountOut;
54631  }
54632 
54633  framesProcessedIn = 0;
54634  framesProcessedOut = 0;
54635 
54636  tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54637  tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54638  tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
54639 
54640  while (framesProcessedOut < frameCountOut) {
54641  ma_uint64 frameCountInThisIteration;
54642  ma_uint64 frameCountOutThisIteration;
54643  const void* pRunningFramesIn = NULL;
54644  void* pRunningFramesOut = NULL;
54645  const void* pResampleBufferIn;
54646  void* pChannelsBufferOut;
54647 
54648  if (pFramesIn != NULL) {
54649  pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
54650  }
54651  if (pFramesOut != NULL) {
54652  pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54653  }
54654 
54655  /* Run input data through the resampler and output it to the temporary buffer. */
54656  frameCountInThisIteration = (frameCountIn - framesProcessedIn);
54657 
54658  if (pConverter->hasPreFormatConversion) {
54659  if (frameCountInThisIteration > tempBufferInCap) {
54660  frameCountInThisIteration = tempBufferInCap;
54661  }
54662  }
54663 
54664  frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
54665  if (frameCountOutThisIteration > tempBufferMidCap) {
54666  frameCountOutThisIteration = tempBufferMidCap;
54667  }
54668 
54669  /* We can't read more frames than can fit in the output buffer. */
54670  if (pConverter->hasPostFormatConversion) {
54671  if (frameCountOutThisIteration > tempBufferOutCap) {
54672  frameCountOutThisIteration = tempBufferOutCap;
54673  }
54674  }
54675 
54676  /* 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. */
54677 
54678  /*
54679  We need to try to predict how many input frames will be required for the resampler. If the
54680  resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further
54681  off we are from this, the more wasted format conversions we'll end up doing.
54682  */
54683  #if 1
54684  {
54685  ma_uint64 requiredInputFrameCount;
54686 
54687  result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
54688  if (result != MA_SUCCESS) {
54689  /* Fall back to a best guess. */
54690  requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
54691  }
54692 
54693  if (frameCountInThisIteration > requiredInputFrameCount) {
54694  frameCountInThisIteration = requiredInputFrameCount;
54695  }
54696  }
54697  #endif
54698 
54699  if (pConverter->hasPreFormatConversion) {
54700  if (pFramesIn != NULL) {
54701  ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
54702  pResampleBufferIn = pTempBufferIn;
54703  } else {
54704  pResampleBufferIn = NULL;
54705  }
54706  } else {
54707  pResampleBufferIn = pRunningFramesIn;
54708  }
54709 
54710  result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
54711  if (result != MA_SUCCESS) {
54712  return result;
54713  }
54714 
54715 
54716  /*
54717  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
54718  this part if we have an output buffer.
54719  */
54720  if (pFramesOut != NULL) {
54721  if (pConverter->hasPostFormatConversion) {
54722  pChannelsBufferOut = pTempBufferOut;
54723  } else {
54724  pChannelsBufferOut = pRunningFramesOut;
54725  }
54726 
54727  result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
54728  if (result != MA_SUCCESS) {
54729  return result;
54730  }
54731 
54732  /* Finally we do post format conversion. */
54733  if (pConverter->hasPostFormatConversion) {
54734  ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
54735  }
54736  }
54737 
54738 
54739  framesProcessedIn += frameCountInThisIteration;
54740  framesProcessedOut += frameCountOutThisIteration;
54741 
54742  MA_ASSERT(framesProcessedIn <= frameCountIn);
54743  MA_ASSERT(framesProcessedOut <= frameCountOut);
54744 
54745  if (frameCountOutThisIteration == 0) {
54746  break; /* Consumed all of our input data. */
54747  }
54748  }
54749 
54750  if (pFrameCountIn != NULL) {
54751  *pFrameCountIn = framesProcessedIn;
54752  }
54753  if (pFrameCountOut != NULL) {
54754  *pFrameCountOut = framesProcessedOut;
54755  }
54756 
54757  return MA_SUCCESS;
54758 }
54759 
54760 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)
54761 {
54762  ma_result result;
54763  ma_uint64 frameCountIn;
54764  ma_uint64 frameCountOut;
54765  ma_uint64 framesProcessedIn;
54766  ma_uint64 framesProcessedOut;
54767  ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
54768  ma_uint64 tempBufferInCap;
54769  ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
54770  ma_uint64 tempBufferMidCap;
54771  ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
54772  ma_uint64 tempBufferOutCap;
54773 
54774  MA_ASSERT(pConverter != NULL);
54775  MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
54776  MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut);
54777  MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn);
54778 
54779  frameCountIn = 0;
54780  if (pFrameCountIn != NULL) {
54781  frameCountIn = *pFrameCountIn;
54782  }
54783 
54784  frameCountOut = 0;
54785  if (pFrameCountOut != NULL) {
54786  frameCountOut = *pFrameCountOut;
54787  }
54788 
54789  framesProcessedIn = 0;
54790  framesProcessedOut = 0;
54791 
54792  tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
54793  tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
54794  tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54795 
54796  while (framesProcessedOut < frameCountOut) {
54797  ma_uint64 frameCountInThisIteration;
54798  ma_uint64 frameCountOutThisIteration;
54799  const void* pRunningFramesIn = NULL;
54800  void* pRunningFramesOut = NULL;
54801  const void* pChannelsBufferIn;
54802  void* pResampleBufferOut;
54803 
54804  if (pFramesIn != NULL) {
54805  pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
54806  }
54807  if (pFramesOut != NULL) {
54808  pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54809  }
54810 
54811  /*
54812  Before doing any processing we need to determine how many frames we should try processing
54813  this iteration, for both input and output. The resampler requires us to perform format and
54814  channel conversion before passing any data into it. If we get our input count wrong, we'll
54815  end up peforming redundant pre-processing. This isn't the end of the world, but it does
54816  result in some inefficiencies proportionate to how far our estimates are off.
54817 
54818  If the resampler has a means to calculate exactly how much we'll need, we'll use that.
54819  Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output
54820  frame count first.
54821  */
54822  frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
54823  if (frameCountOutThisIteration > tempBufferMidCap) {
54824  frameCountOutThisIteration = tempBufferMidCap;
54825  }
54826 
54827  if (pConverter->hasPostFormatConversion) {
54828  if (frameCountOutThisIteration > tempBufferOutCap) {
54829  frameCountOutThisIteration = tempBufferOutCap;
54830  }
54831  }
54832 
54833  /* Now that we have the output frame count we can determine the input frame count. */
54834  frameCountInThisIteration = (frameCountIn - framesProcessedIn);
54835  if (pConverter->hasPreFormatConversion) {
54836  if (frameCountInThisIteration > tempBufferInCap) {
54837  frameCountInThisIteration = tempBufferInCap;
54838  }
54839  }
54840 
54841  if (frameCountInThisIteration > tempBufferMidCap) {
54842  frameCountInThisIteration = tempBufferMidCap;
54843  }
54844 
54845  #if 1
54846  {
54847  ma_uint64 requiredInputFrameCount;
54848 
54849  result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
54850  if (result != MA_SUCCESS) {
54851  /* Fall back to a best guess. */
54852  requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
54853  }
54854 
54855  if (frameCountInThisIteration > requiredInputFrameCount) {
54856  frameCountInThisIteration = requiredInputFrameCount;
54857  }
54858  }
54859  #endif
54860 
54861 
54862  /* Pre format conversion. */
54863  if (pConverter->hasPreFormatConversion) {
54864  if (pRunningFramesIn != NULL) {
54865  ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
54866  pChannelsBufferIn = pTempBufferIn;
54867  } else {
54868  pChannelsBufferIn = NULL;
54869  }
54870  } else {
54871  pChannelsBufferIn = pRunningFramesIn;
54872  }
54873 
54874 
54875  /* Channel conversion. */
54876  result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
54877  if (result != MA_SUCCESS) {
54878  return result;
54879  }
54880 
54881 
54882  /* Resampling. */
54883  if (pConverter->hasPostFormatConversion) {
54884  pResampleBufferOut = pTempBufferOut;
54885  } else {
54886  pResampleBufferOut = pRunningFramesOut;
54887  }
54888 
54889  result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
54890  if (result != MA_SUCCESS) {
54891  return result;
54892  }
54893 
54894 
54895  /* Post format conversion. */
54896  if (pConverter->hasPostFormatConversion) {
54897  if (pRunningFramesOut != NULL) {
54898  ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode);
54899  }
54900  }
54901 
54902 
54903  framesProcessedIn += frameCountInThisIteration;
54904  framesProcessedOut += frameCountOutThisIteration;
54905 
54906  MA_ASSERT(framesProcessedIn <= frameCountIn);
54907  MA_ASSERT(framesProcessedOut <= frameCountOut);
54908 
54909  if (frameCountOutThisIteration == 0) {
54910  break; /* Consumed all of our input data. */
54911  }
54912  }
54913 
54914  if (pFrameCountIn != NULL) {
54915  *pFrameCountIn = framesProcessedIn;
54916  }
54917  if (pFrameCountOut != NULL) {
54918  *pFrameCountOut = framesProcessedOut;
54919  }
54920 
54921  return MA_SUCCESS;
54922 }
54923 
54924 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)
54925 {
54926  if (pConverter == NULL) {
54927  return MA_INVALID_ARGS;
54928  }
54929 
54930  switch (pConverter->executionPath)
54931  {
54932  case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54933  case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54934  case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54935  case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54936  case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54937  case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54938  default: return MA_INVALID_OPERATION; /* Should never hit this. */
54939  }
54940 }
54941 
54942 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
54943 {
54944  if (pConverter == NULL) {
54945  return MA_INVALID_ARGS;
54946  }
54947 
54948  if (pConverter->hasResampler == MA_FALSE) {
54949  return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
54950  }
54951 
54952  return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
54953 }
54954 
54955 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)
54956 {
54957  if (pConverter == NULL) {
54958  return MA_INVALID_ARGS;
54959  }
54960 
54961  if (pConverter->hasResampler == MA_FALSE) {
54962  return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
54963  }
54964 
54965  return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
54966 }
54967 
54968 MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter)
54969 {
54970  if (pConverter == NULL) {
54971  return 0;
54972  }
54973 
54974  if (pConverter->hasResampler) {
54975  return ma_resampler_get_input_latency(&pConverter->resampler);
54976  }
54977 
54978  return 0; /* No latency without a resampler. */
54979 }
54980 
54981 MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter)
54982 {
54983  if (pConverter == NULL) {
54984  return 0;
54985  }
54986 
54987  if (pConverter->hasResampler) {
54988  return ma_resampler_get_output_latency(&pConverter->resampler);
54989  }
54990 
54991  return 0; /* No latency without a resampler. */
54992 }
54993 
54994 MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
54995 {
54996  if (pInputFrameCount == NULL) {
54997  return MA_INVALID_ARGS;
54998  }
54999 
55000  *pInputFrameCount = 0;
55001 
55002  if (pConverter == NULL) {
55003  return MA_INVALID_ARGS;
55004  }
55005 
55006  if (pConverter->hasResampler) {
55007  return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount);
55008  } else {
55009  *pInputFrameCount = outputFrameCount; /* 1:1 */
55010  return MA_SUCCESS;
55011  }
55012 }
55013 
55014 MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
55015 {
55016  if (pOutputFrameCount == NULL) {
55017  return MA_INVALID_ARGS;
55018  }
55019 
55020  *pOutputFrameCount = 0;
55021 
55022  if (pConverter == NULL) {
55023  return MA_INVALID_ARGS;
55024  }
55025 
55026  if (pConverter->hasResampler) {
55027  return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount);
55028  } else {
55029  *pOutputFrameCount = inputFrameCount; /* 1:1 */
55030  return MA_SUCCESS;
55031  }
55032 }
55033 
55034 MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
55035 {
55036  if (pConverter == NULL || pChannelMap == NULL) {
55037  return MA_INVALID_ARGS;
55038  }
55039 
55040  if (pConverter->hasChannelConverter) {
55041  ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
55042  } else {
55043  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut);
55044  }
55045 
55046  return MA_SUCCESS;
55047 }
55048 
55049 MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
55050 {
55051  if (pConverter == NULL || pChannelMap == NULL) {
55052  return MA_INVALID_ARGS;
55053  }
55054 
55055  if (pConverter->hasChannelConverter) {
55056  ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
55057  } else {
55058  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn);
55059  }
55060 
55061  return MA_SUCCESS;
55062 }
55063 
55064 MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter)
55065 {
55066  if (pConverter == NULL) {
55067  return MA_INVALID_ARGS;
55068  }
55069 
55070  /* There's nothing to do if we're not resampling. */
55071  if (pConverter->hasResampler == MA_FALSE) {
55072  return MA_SUCCESS;
55073  }
55074 
55075  return ma_resampler_reset(&pConverter->resampler);
55076 }
55077 
55078 
55079 
55080 /**************************************************************************************************************************************************************
55081 
55082 Channel Maps
55083 
55084 **************************************************************************************************************************************************************/
55085 static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
55086 
55087 MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
55088 {
55089  if (pChannelMap == NULL) {
55090  return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex);
55091  } else {
55092  if (channelIndex >= channelCount) {
55093  return MA_CHANNEL_NONE;
55094  }
55095 
55096  return pChannelMap[channelIndex];
55097  }
55098 }
55099 
55100 MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels)
55101 {
55102  if (pChannelMap == NULL) {
55103  return;
55104  }
55105 
55106  MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);
55107 }
55108 
55109 
55110 static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex)
55111 {
55112  if (channelCount == 0 || channelIndex >= channelCount) {
55113  return MA_CHANNEL_NONE;
55114  }
55115 
55116  /* 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 */
55117  switch (channelCount)
55118  {
55119  case 0: return MA_CHANNEL_NONE;
55120 
55121  case 1:
55122  {
55123  return MA_CHANNEL_MONO;
55124  } break;
55125 
55126  case 2:
55127  {
55128  switch (channelIndex) {
55129  case 0: return MA_CHANNEL_FRONT_LEFT;
55130  case 1: return MA_CHANNEL_FRONT_RIGHT;
55131  }
55132  } break;
55133 
55134  case 3: /* No defined, but best guess. */
55135  {
55136  switch (channelIndex) {
55137  case 0: return MA_CHANNEL_FRONT_LEFT;
55138  case 1: return MA_CHANNEL_FRONT_RIGHT;
55139  case 2: return MA_CHANNEL_FRONT_CENTER;
55140  }
55141  } break;
55142 
55143  case 4:
55144  {
55145  switch (channelIndex) {
55146  #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
55147  /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
55148  case 0: return MA_CHANNEL_FRONT_LEFT;
55149  case 1: return MA_CHANNEL_FRONT_RIGHT;
55150  case 2: return MA_CHANNEL_FRONT_CENTER;
55151  case 3: return MA_CHANNEL_BACK_CENTER;
55152  #else
55153  /* Quad. */
55154  case 0: return MA_CHANNEL_FRONT_LEFT;
55155  case 1: return MA_CHANNEL_FRONT_RIGHT;
55156  case 2: return MA_CHANNEL_BACK_LEFT;
55157  case 3: return MA_CHANNEL_BACK_RIGHT;
55158  #endif
55159  }
55160  } break;
55161 
55162  case 5: /* Not defined, but best guess. */
55163  {
55164  switch (channelIndex) {
55165  case 0: return MA_CHANNEL_FRONT_LEFT;
55166  case 1: return MA_CHANNEL_FRONT_RIGHT;
55167  case 2: return MA_CHANNEL_FRONT_CENTER;
55168  case 3: return MA_CHANNEL_BACK_LEFT;
55169  case 4: return MA_CHANNEL_BACK_RIGHT;
55170  }
55171  } break;
55172 
55173  case 6:
55174  {
55175  switch (channelIndex) {
55176  case 0: return MA_CHANNEL_FRONT_LEFT;
55177  case 1: return MA_CHANNEL_FRONT_RIGHT;
55178  case 2: return MA_CHANNEL_FRONT_CENTER;
55179  case 3: return MA_CHANNEL_LFE;
55180  case 4: return MA_CHANNEL_SIDE_LEFT;
55181  case 5: return MA_CHANNEL_SIDE_RIGHT;
55182  }
55183  } break;
55184 
55185  case 7: /* Not defined, but best guess. */
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_BACK_CENTER;
55193  case 5: return MA_CHANNEL_SIDE_LEFT;
55194  case 6: return MA_CHANNEL_SIDE_RIGHT;
55195  }
55196  } break;
55197 
55198  case 8:
55199  default:
55200  {
55201  switch (channelIndex) {
55202  case 0: return MA_CHANNEL_FRONT_LEFT;
55203  case 1: return MA_CHANNEL_FRONT_RIGHT;
55204  case 2: return MA_CHANNEL_FRONT_CENTER;
55205  case 3: return MA_CHANNEL_LFE;
55206  case 4: return MA_CHANNEL_BACK_LEFT;
55207  case 5: return MA_CHANNEL_BACK_RIGHT;
55208  case 6: return MA_CHANNEL_SIDE_LEFT;
55209  case 7: return MA_CHANNEL_SIDE_RIGHT;
55210  }
55211  } break;
55212  }
55213 
55214  if (channelCount > 8) {
55215  if (channelIndex < 32) { /* We have 32 AUX channels. */
55216  return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55217  }
55218  }
55219 
55220  /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55221  return MA_CHANNEL_NONE;
55222 }
55223 
55224 static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex)
55225 {
55226  switch (channelCount)
55227  {
55228  case 0: return MA_CHANNEL_NONE;
55229 
55230  case 1:
55231  {
55232  return MA_CHANNEL_MONO;
55233  } break;
55234 
55235  case 2:
55236  {
55237  switch (channelIndex) {
55238  case 0: return MA_CHANNEL_FRONT_LEFT;
55239  case 1: return MA_CHANNEL_FRONT_RIGHT;
55240  }
55241  } break;
55242 
55243  case 3:
55244  {
55245  switch (channelIndex) {
55246  case 0: return MA_CHANNEL_FRONT_LEFT;
55247  case 1: return MA_CHANNEL_FRONT_RIGHT;
55248  case 2: return MA_CHANNEL_FRONT_CENTER;
55249  }
55250  } break;
55251 
55252  case 4:
55253  {
55254  switch (channelIndex) {
55255  case 0: return MA_CHANNEL_FRONT_LEFT;
55256  case 1: return MA_CHANNEL_FRONT_RIGHT;
55257  case 2: return MA_CHANNEL_BACK_LEFT;
55258  case 3: return MA_CHANNEL_BACK_RIGHT;
55259  }
55260  } break;
55261 
55262  case 5:
55263  {
55264  switch (channelIndex) {
55265  case 0: return MA_CHANNEL_FRONT_LEFT;
55266  case 1: return MA_CHANNEL_FRONT_RIGHT;
55267  case 2: return MA_CHANNEL_BACK_LEFT;
55268  case 3: return MA_CHANNEL_BACK_RIGHT;
55269  case 4: return MA_CHANNEL_FRONT_CENTER;
55270  }
55271  } break;
55272 
55273  case 6:
55274  {
55275  switch (channelIndex) {
55276  case 0: return MA_CHANNEL_FRONT_LEFT;
55277  case 1: return MA_CHANNEL_FRONT_RIGHT;
55278  case 2: return MA_CHANNEL_BACK_LEFT;
55279  case 3: return MA_CHANNEL_BACK_RIGHT;
55280  case 4: return MA_CHANNEL_FRONT_CENTER;
55281  case 5: return MA_CHANNEL_LFE;
55282  }
55283  } break;
55284 
55285  case 7:
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  case 6: return MA_CHANNEL_BACK_CENTER;
55295  }
55296  } break;
55297 
55298  case 8:
55299  default:
55300  {
55301  switch (channelIndex) {
55302  case 0: return MA_CHANNEL_FRONT_LEFT;
55303  case 1: return MA_CHANNEL_FRONT_RIGHT;
55304  case 2: return MA_CHANNEL_BACK_LEFT;
55305  case 3: return MA_CHANNEL_BACK_RIGHT;
55306  case 4: return MA_CHANNEL_FRONT_CENTER;
55307  case 5: return MA_CHANNEL_LFE;
55308  case 6: return MA_CHANNEL_SIDE_LEFT;
55309  case 7: return MA_CHANNEL_SIDE_RIGHT;
55310  }
55311  } break;
55312  }
55313 
55314  if (channelCount > 8) {
55315  if (channelIndex < 32) { /* We have 32 AUX channels. */
55316  return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55317  }
55318  }
55319 
55320  /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55321  return MA_CHANNEL_NONE;
55322 }
55323 
55324 static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex)
55325 {
55326  switch (channelCount)
55327  {
55328  case 0: return MA_CHANNEL_NONE;
55329 
55330  case 1:
55331  {
55332  return MA_CHANNEL_MONO;
55333  } break;
55334 
55335  case 2:
55336  {
55337  switch (channelIndex) {
55338  case 0: return MA_CHANNEL_FRONT_LEFT;
55339  case 1: return MA_CHANNEL_FRONT_RIGHT;
55340  }
55341  } break;
55342 
55343  case 3:
55344  {
55345  switch (channelIndex) {
55346  case 0: return MA_CHANNEL_FRONT_LEFT;
55347  case 1: return MA_CHANNEL_FRONT_RIGHT;
55348  case 2: return MA_CHANNEL_FRONT_CENTER;
55349  }
55350  } break;
55351 
55352  case 4:
55353  {
55354  switch (channelIndex) {
55355  case 0: return MA_CHANNEL_FRONT_LEFT;
55356  case 2: return MA_CHANNEL_FRONT_CENTER;
55357  case 1: return MA_CHANNEL_FRONT_RIGHT;
55358  case 3: return MA_CHANNEL_BACK_CENTER;
55359  }
55360  } break;
55361 
55362  case 5:
55363  {
55364  switch (channelIndex) {
55365  case 0: return MA_CHANNEL_FRONT_LEFT;
55366  case 1: return MA_CHANNEL_FRONT_RIGHT;
55367  case 2: return MA_CHANNEL_FRONT_CENTER;
55368  case 3: return MA_CHANNEL_BACK_LEFT;
55369  case 4: return MA_CHANNEL_BACK_RIGHT;
55370  }
55371  } break;
55372 
55373  case 6:
55374  default:
55375  {
55376  switch (channelIndex) {
55377  case 0: return MA_CHANNEL_FRONT_LEFT;
55378  case 1: return MA_CHANNEL_SIDE_LEFT;
55379  case 2: return MA_CHANNEL_FRONT_CENTER;
55380  case 3: return MA_CHANNEL_FRONT_RIGHT;
55381  case 4: return MA_CHANNEL_SIDE_RIGHT;
55382  case 5: return MA_CHANNEL_BACK_CENTER;
55383  }
55384  } break;
55385  }
55386 
55387  if (channelCount > 6) {
55388  if (channelIndex < 32) { /* We have 32 AUX channels. */
55389  return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
55390  }
55391  }
55392 
55393  /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55394  return MA_CHANNEL_NONE;
55395 }
55396 
55397 static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex)
55398 {
55399  switch (channelCount)
55400  {
55401  case 0: return MA_CHANNEL_NONE;
55402 
55403  case 1:
55404  {
55405  return MA_CHANNEL_MONO;
55406  } break;
55407 
55408  case 2:
55409  {
55410  switch (channelIndex) {
55411  case 0: return MA_CHANNEL_FRONT_LEFT;
55412  case 1: return MA_CHANNEL_FRONT_RIGHT;
55413  }
55414  } break;
55415 
55416  case 3:
55417  {
55418  switch (channelIndex) {
55419  case 0: return MA_CHANNEL_FRONT_LEFT;
55420  case 1: return MA_CHANNEL_FRONT_RIGHT;
55421  case 2: return MA_CHANNEL_FRONT_CENTER;
55422  }
55423  } break;
55424 
55425  case 4:
55426  {
55427  switch (channelIndex) {
55428  case 0: return MA_CHANNEL_FRONT_LEFT;
55429  case 1: return MA_CHANNEL_FRONT_RIGHT;
55430  case 2: return MA_CHANNEL_BACK_LEFT;
55431  case 3: return MA_CHANNEL_BACK_RIGHT;
55432  }
55433  } break;
55434 
55435  case 5:
55436  {
55437  switch (channelIndex) {
55438  case 0: return MA_CHANNEL_FRONT_LEFT;
55439  case 1: return MA_CHANNEL_FRONT_RIGHT;
55440  case 2: return MA_CHANNEL_FRONT_CENTER;
55441  case 3: return MA_CHANNEL_BACK_LEFT;
55442  case 4: return MA_CHANNEL_BACK_RIGHT;
55443  }
55444  } break;
55445 
55446  case 6:
55447  {
55448  switch (channelIndex) {
55449  case 0: return MA_CHANNEL_FRONT_LEFT;
55450  case 1: return MA_CHANNEL_FRONT_RIGHT;
55451  case 2: return MA_CHANNEL_FRONT_CENTER;
55452  case 3: return MA_CHANNEL_LFE;
55453  case 4: return MA_CHANNEL_BACK_LEFT;
55454  case 5: return MA_CHANNEL_BACK_RIGHT;
55455  }
55456  } break;
55457 
55458  case 7:
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_CENTER;
55466  case 5: return MA_CHANNEL_SIDE_LEFT;
55467  case 6: return MA_CHANNEL_SIDE_RIGHT;
55468  }
55469  } break;
55470 
55471  case 8:
55472  default:
55473  {
55474  switch (channelIndex) {
55475  case 0: return MA_CHANNEL_FRONT_LEFT;
55476  case 1: return MA_CHANNEL_FRONT_RIGHT;
55477  case 2: return MA_CHANNEL_FRONT_CENTER;
55478  case 3: return MA_CHANNEL_LFE;
55479  case 4: return MA_CHANNEL_BACK_LEFT;
55480  case 5: return MA_CHANNEL_BACK_RIGHT;
55481  case 6: return MA_CHANNEL_SIDE_LEFT;
55482  case 7: return MA_CHANNEL_SIDE_RIGHT;
55483  }
55484  } break;
55485  }
55486 
55487  if (channelCount > 8) {
55488  if (channelIndex < 32) { /* We have 32 AUX channels. */
55489  return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55490  }
55491  }
55492 
55493  /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55494  return MA_CHANNEL_NONE;
55495 }
55496 
55497 static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex)
55498 {
55499  switch (channelCount)
55500  {
55501  case 0: return MA_CHANNEL_NONE;
55502 
55503  case 1:
55504  {
55505  return MA_CHANNEL_MONO;
55506  } break;
55507 
55508  case 2:
55509  {
55510  switch (channelIndex) {
55511  case 0: return MA_CHANNEL_FRONT_LEFT;
55512  case 1: return MA_CHANNEL_FRONT_RIGHT;
55513  }
55514  } break;
55515 
55516  case 3:
55517  {
55518  switch (channelIndex) {
55519  case 0: return MA_CHANNEL_FRONT_LEFT;
55520  case 1: return MA_CHANNEL_FRONT_CENTER;
55521  case 2: return MA_CHANNEL_FRONT_RIGHT;
55522  }
55523  } break;
55524 
55525  case 4:
55526  {
55527  switch (channelIndex) {
55528  case 0: return MA_CHANNEL_FRONT_LEFT;
55529  case 1: return MA_CHANNEL_FRONT_RIGHT;
55530  case 2: return MA_CHANNEL_BACK_LEFT;
55531  case 3: return MA_CHANNEL_BACK_RIGHT;
55532  }
55533  } break;
55534 
55535  case 5:
55536  {
55537  switch (channelIndex) {
55538  case 0: return MA_CHANNEL_FRONT_LEFT;
55539  case 1: return MA_CHANNEL_FRONT_CENTER;
55540  case 2: return MA_CHANNEL_FRONT_RIGHT;
55541  case 3: return MA_CHANNEL_BACK_LEFT;
55542  case 4: return MA_CHANNEL_BACK_RIGHT;
55543  }
55544  } break;
55545 
55546  case 6:
55547  {
55548  switch (channelIndex) {
55549  case 0: return MA_CHANNEL_FRONT_LEFT;
55550  case 1: return MA_CHANNEL_FRONT_CENTER;
55551  case 2: return MA_CHANNEL_FRONT_RIGHT;
55552  case 3: return MA_CHANNEL_BACK_LEFT;
55553  case 4: return MA_CHANNEL_BACK_RIGHT;
55554  case 5: return MA_CHANNEL_LFE;
55555  }
55556  } break;
55557 
55558  case 7:
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_SIDE_LEFT;
55565  case 4: return MA_CHANNEL_SIDE_RIGHT;
55566  case 5: return MA_CHANNEL_BACK_CENTER;
55567  case 6: return MA_CHANNEL_LFE;
55568  }
55569  } break;
55570 
55571  case 8:
55572  default:
55573  {
55574  switch (channelIndex) {
55575  case 0: return MA_CHANNEL_FRONT_LEFT;
55576  case 1: return MA_CHANNEL_FRONT_CENTER;
55577  case 2: return MA_CHANNEL_FRONT_RIGHT;
55578  case 3: return MA_CHANNEL_SIDE_LEFT;
55579  case 4: return MA_CHANNEL_SIDE_RIGHT;
55580  case 5: return MA_CHANNEL_BACK_LEFT;
55581  case 6: return MA_CHANNEL_BACK_RIGHT;
55582  case 7: return MA_CHANNEL_LFE;
55583  }
55584  } break;
55585  }
55586 
55587  if (channelCount > 8) {
55588  if (channelIndex < 32) { /* We have 32 AUX channels. */
55589  return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55590  }
55591  }
55592 
55593  /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55594  return MA_CHANNEL_NONE;
55595 }
55596 
55597 static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex)
55598 {
55599  switch (channelCount)
55600  {
55601  case 0: return MA_CHANNEL_NONE;
55602 
55603  case 1:
55604  {
55605  return MA_CHANNEL_MONO;
55606  } break;
55607 
55608  case 2:
55609  {
55610  switch (channelIndex) {
55611  case 0: return MA_CHANNEL_FRONT_LEFT;
55612  case 1: return MA_CHANNEL_FRONT_RIGHT;
55613  }
55614  } break;
55615 
55616  case 3:
55617  {
55618  switch (channelIndex) {
55619  case 0: return MA_CHANNEL_FRONT_LEFT;
55620  case 1: return MA_CHANNEL_FRONT_RIGHT;
55621  case 2: return MA_CHANNEL_FRONT_CENTER;
55622  }
55623  } break;
55624 
55625  case 4:
55626  {
55627  switch (channelIndex) {
55628  case 0: return MA_CHANNEL_FRONT_LEFT;
55629  case 1: return MA_CHANNEL_FRONT_RIGHT;
55630  case 2: return MA_CHANNEL_BACK_LEFT;
55631  case 3: return MA_CHANNEL_BACK_RIGHT;
55632  }
55633  } break;
55634 
55635  case 5:
55636  {
55637  switch (channelIndex) {
55638  case 0: return MA_CHANNEL_FRONT_LEFT;
55639  case 1: return MA_CHANNEL_FRONT_RIGHT;
55640  case 2: return MA_CHANNEL_FRONT_CENTER;
55641  case 3: return MA_CHANNEL_BACK_LEFT;
55642  case 4: return MA_CHANNEL_BACK_RIGHT;
55643  }
55644  } break;
55645 
55646  case 6:
55647  {
55648  switch (channelIndex) {
55649  case 0: return MA_CHANNEL_FRONT_LEFT;
55650  case 1: return MA_CHANNEL_FRONT_CENTER;
55651  case 2: return MA_CHANNEL_FRONT_RIGHT;
55652  case 3: return MA_CHANNEL_BACK_LEFT;
55653  case 4: return MA_CHANNEL_BACK_RIGHT;
55654  case 5: return MA_CHANNEL_LFE;
55655  }
55656  } break;
55657 
55658  case 7:
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_SIDE_LEFT;
55665  case 4: return MA_CHANNEL_SIDE_RIGHT;
55666  case 5: return MA_CHANNEL_BACK_CENTER;
55667  case 6: return MA_CHANNEL_LFE;
55668  }
55669  } break;
55670 
55671  case 8:
55672  default:
55673  {
55674  switch (channelIndex) {
55675  case 0: return MA_CHANNEL_FRONT_LEFT;
55676  case 1: return MA_CHANNEL_FRONT_CENTER;
55677  case 2: return MA_CHANNEL_FRONT_RIGHT;
55678  case 3: return MA_CHANNEL_SIDE_LEFT;
55679  case 4: return MA_CHANNEL_SIDE_RIGHT;
55680  case 5: return MA_CHANNEL_BACK_LEFT;
55681  case 6: return MA_CHANNEL_BACK_RIGHT;
55682  case 7: return MA_CHANNEL_LFE;
55683  }
55684  } break;
55685  }
55686 
55687  if (channelCount > 8) {
55688  if (channelIndex < 32) { /* We have 32 AUX channels. */
55689  return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55690  }
55691  }
55692 
55693  /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55694  return MA_CHANNEL_NONE;
55695 }
55696 
55697 static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex)
55698 {
55699  switch (channelCount)
55700  {
55701  case 0: return MA_CHANNEL_NONE;
55702 
55703  case 1:
55704  {
55705  return MA_CHANNEL_MONO;
55706  } break;
55707 
55708  case 2:
55709  {
55710  switch (channelIndex) {
55711  case 0: return MA_CHANNEL_FRONT_LEFT;
55712  case 1: return MA_CHANNEL_FRONT_RIGHT;
55713  }
55714  } break;
55715 
55716  case 3: /* No defined, but best guess. */
55717  {
55718  switch (channelIndex) {
55719  case 0: return MA_CHANNEL_FRONT_LEFT;
55720  case 1: return MA_CHANNEL_FRONT_RIGHT;
55721  case 2: return MA_CHANNEL_FRONT_CENTER;
55722  }
55723  } break;
55724 
55725  case 4:
55726  {
55727  switch (channelIndex) {
55728  case 0: return MA_CHANNEL_FRONT_LEFT;
55729  case 1: return MA_CHANNEL_FRONT_RIGHT;
55730  case 2: return MA_CHANNEL_BACK_LEFT;
55731  case 3: return MA_CHANNEL_BACK_RIGHT;
55732  }
55733  } break;
55734 
55735  case 5: /* Not defined, but best guess. */
55736  {
55737  switch (channelIndex) {
55738  case 0: return MA_CHANNEL_FRONT_LEFT;
55739  case 1: return MA_CHANNEL_FRONT_RIGHT;
55740  case 2: return MA_CHANNEL_BACK_LEFT;
55741  case 3: return MA_CHANNEL_BACK_RIGHT;
55742  case 4: return MA_CHANNEL_FRONT_CENTER;
55743  }
55744  } break;
55745 
55746  case 6:
55747  default:
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  case 5: return MA_CHANNEL_LFE;
55756  }
55757  } break;
55758  }
55759 
55760  if (channelCount > 6) {
55761  if (channelIndex < 32) { /* We have 32 AUX channels. */
55762  return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
55763  }
55764  }
55765 
55766  /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55767  return MA_CHANNEL_NONE;
55768 }
55769 
55770 
55771 static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
55772 {
55773  if (channelCount == 0 || channelIndex >= channelCount) {
55774  return MA_CHANNEL_NONE;
55775  }
55776 
55777  switch (standardChannelMap)
55778  {
55779  case ma_standard_channel_map_alsa:
55780  {
55781  return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex);
55782  } break;
55783 
55784  case ma_standard_channel_map_rfc3551:
55785  {
55786  return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex);
55787  } break;
55788 
55789  case ma_standard_channel_map_flac:
55790  {
55791  return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex);
55792  } break;
55793 
55794  case ma_standard_channel_map_vorbis:
55795  {
55796  return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex);
55797  } break;
55798 
55799  case ma_standard_channel_map_sound4:
55800  {
55801  return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex);
55802  } break;
55803 
55804  case ma_standard_channel_map_sndio:
55805  {
55806  return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex);
55807  } break;
55808 
55809  case ma_standard_channel_map_microsoft: /* Also default. */
55810  /*case ma_standard_channel_map_default;*/
55811  default:
55812  {
55813  return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex);
55814  } break;
55815  }
55816 }
55817 
55818 MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels)
55819 {
55820  ma_uint32 iChannel;
55821 
55822  if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) {
55823  return;
55824  }
55825 
55826  for (iChannel = 0; iChannel < channels; iChannel += 1) {
55827  if (channelMapCap == 0) {
55828  break; /* Ran out of room. */
55829  }
55830 
55831  pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel);
55832  pChannelMap += 1;
55833  channelMapCap -= 1;
55834  }
55835 }
55836 
55837 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
55838 {
55839  if (pOut != NULL && pIn != NULL && channels > 0) {
55840  MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
55841  }
55842 }
55843 
55844 MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels)
55845 {
55846  if (pOut == NULL || channels == 0) {
55847  return;
55848  }
55849 
55850  if (pIn != NULL) {
55851  ma_channel_map_copy(pOut, pIn, channels);
55852  } else {
55853  ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels);
55854  }
55855 }
55856 
55857 MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels)
55858 {
55859  /* A channel count of 0 is invalid. */
55860  if (channels == 0) {
55861  return MA_FALSE;
55862  }
55863 
55864  /* It does not make sense to have a mono channel when there is more than 1 channel. */
55865  if (channels > 1) {
55866  ma_uint32 iChannel;
55867  for (iChannel = 0; iChannel < channels; ++iChannel) {
55868  if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) {
55869  return MA_FALSE;
55870  }
55871  }
55872  }
55873 
55874  return MA_TRUE;
55875 }
55876 
55877 MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels)
55878 {
55879  ma_uint32 iChannel;
55880 
55881  if (pChannelMapA == pChannelMapB) {
55882  return MA_TRUE;
55883  }
55884 
55885  for (iChannel = 0; iChannel < channels; ++iChannel) {
55886  if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) {
55887  return MA_FALSE;
55888  }
55889  }
55890 
55891  return MA_TRUE;
55892 }
55893 
55894 MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels)
55895 {
55896  ma_uint32 iChannel;
55897 
55898  /* A null channel map is equivalent to the default channel map. */
55899  if (pChannelMap == NULL) {
55900  return MA_FALSE;
55901  }
55902 
55903  for (iChannel = 0; iChannel < channels; ++iChannel) {
55904  if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {
55905  return MA_FALSE;
55906  }
55907  }
55908 
55909  return MA_TRUE;
55910 }
55911 
55912 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)
55913 {
55914  return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL);
55915 }
55916 
55917 MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex)
55918 {
55919  ma_uint32 iChannel;
55920 
55921  if (pChannelIndex != NULL) {
55922  *pChannelIndex = (ma_uint32)-1;
55923  }
55924 
55925  for (iChannel = 0; iChannel < channels; ++iChannel) {
55926  if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) {
55927  if (pChannelIndex != NULL) {
55928  *pChannelIndex = iChannel;
55929  }
55930 
55931  return MA_TRUE;
55932  }
55933  }
55934 
55935  /* Getting here means the channel position was not found. */
55936  return MA_FALSE;
55937 }
55938 
55939 MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap)
55940 {
55941  size_t len;
55942  ma_uint32 iChannel;
55943 
55944  len = 0;
55945 
55946  for (iChannel = 0; iChannel < channels; iChannel += 1) {
55947  const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel));
55948  size_t channelStrLen = strlen(pChannelStr);
55949 
55950  /* Append the string if necessary. */
55951  if (pBufferOut != NULL && bufferCap > len + channelStrLen) {
55952  MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen);
55953  }
55954  len += channelStrLen;
55955 
55956  /* Append a space if it's not the last item. */
55957  if (iChannel+1 < channels) {
55958  if (pBufferOut != NULL && bufferCap > len + 1) {
55959  pBufferOut[len] = ' ';
55960  }
55961  len += 1;
55962  }
55963  }
55964 
55965  /* Null terminate. Don't increment the length here. */
55966  if (pBufferOut != NULL && bufferCap > len + 1) {
55967  pBufferOut[len] = '\0';
55968  }
55969 
55970  return len;
55971 }
55972 
55973 MA_API const char* ma_channel_position_to_string(ma_channel channel)
55974 {
55975  switch (channel)
55976  {
55977  case MA_CHANNEL_NONE : return "CHANNEL_NONE";
55978  case MA_CHANNEL_MONO : return "CHANNEL_MONO";
55979  case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT";
55980  case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT";
55981  case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER";
55982  case MA_CHANNEL_LFE : return "CHANNEL_LFE";
55983  case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT";
55984  case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT";
55985  case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER ";
55986  case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER";
55987  case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER";
55988  case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT";
55989  case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT";
55990  case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER";
55991  case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT";
55992  case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER";
55993  case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT";
55994  case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT";
55995  case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER";
55996  case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT";
55997  case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0";
55998  case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1";
55999  case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2";
56000  case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3";
56001  case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4";
56002  case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5";
56003  case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6";
56004  case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7";
56005  case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8";
56006  case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9";
56007  case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10";
56008  case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11";
56009  case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12";
56010  case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13";
56011  case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14";
56012  case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15";
56013  case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16";
56014  case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17";
56015  case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18";
56016  case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19";
56017  case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20";
56018  case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21";
56019  case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22";
56020  case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23";
56021  case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24";
56022  case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25";
56023  case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26";
56024  case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27";
56025  case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28";
56026  case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29";
56027  case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30";
56028  case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31";
56029  default: break;
56030  }
56031 
56032  return "UNKNOWN";
56033 }
56034 
56035 
56036 
56037 /**************************************************************************************************************************************************************
56038 
56039 Conversion Helpers
56040 
56041 **************************************************************************************************************************************************************/
56042 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)
56043 {
56044  ma_data_converter_config config;
56045 
56046  config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
56047  config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
56048 
56049  return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
56050 }
56051 
56052 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)
56053 {
56054  ma_result result;
56055  ma_data_converter converter;
56056 
56057  if (frameCountIn == 0 || pConfig == NULL) {
56058  return 0;
56059  }
56060 
56061  result = ma_data_converter_init(pConfig, NULL, &converter);
56062  if (result != MA_SUCCESS) {
56063  return 0; /* Failed to initialize the data converter. */
56064  }
56065 
56066  if (pOut == NULL) {
56067  result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut);
56068  if (result != MA_SUCCESS) {
56069  if (result == MA_NOT_IMPLEMENTED) {
56070  /* No way to calculate the number of frames, so we'll need to brute force it and loop. */
56071  frameCountOut = 0;
56072 
56073  while (frameCountIn > 0) {
56074  ma_uint64 framesProcessedIn = frameCountIn;
56075  ma_uint64 framesProcessedOut = 0xFFFFFFFF;
56076 
56077  result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut);
56078  if (result != MA_SUCCESS) {
56079  break;
56080  }
56081 
56082  frameCountIn -= framesProcessedIn;
56083  }
56084  }
56085  }
56086  } else {
56087  result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
56088  if (result != MA_SUCCESS) {
56089  frameCountOut = 0;
56090  }
56091  }
56092 
56093  ma_data_converter_uninit(&converter, NULL);
56094  return frameCountOut;
56095 }
56096 
56097 
56098 /**************************************************************************************************************************************************************
56099 
56100 Ring Buffer
56101 
56102 **************************************************************************************************************************************************************/
56103 static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
56104 {
56105  return encodedOffset & 0x7FFFFFFF;
56106 }
56107 
56108 static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
56109 {
56110  return encodedOffset & 0x80000000;
56111 }
56112 
56113 static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
56114 {
56115  MA_ASSERT(pRB != NULL);
56116  return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset)));
56117 }
56118 
56119 static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
56120 {
56121  MA_ASSERT(pRB != NULL);
56122  return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset)));
56123 }
56124 
56125 static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
56126 {
56127  return offsetLoopFlag | offsetInBytes;
56128 }
56129 
56130 static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
56131 {
56132  MA_ASSERT(pOffsetInBytes != NULL);
56133  MA_ASSERT(pOffsetLoopFlag != NULL);
56134 
56135  *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
56136  *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
56137 }
56138 
56139 
56140 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)
56141 {
56142  ma_result result;
56143  const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
56144 
56145  if (pRB == NULL) {
56146  return MA_INVALID_ARGS;
56147  }
56148 
56149  if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
56150  return MA_INVALID_ARGS;
56151  }
56152 
56153  if (subbufferSizeInBytes > maxSubBufferSize) {
56154  return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
56155  }
56156 
56157 
56158  MA_ZERO_OBJECT(pRB);
56159 
56160  result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
56161  if (result != MA_SUCCESS) {
56162  return result;
56163  }
56164 
56165  pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
56166  pRB->subbufferCount = (ma_uint32)subbufferCount;
56167 
56168  if (pOptionalPreallocatedBuffer != NULL) {
56169  pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
56170  pRB->pBuffer = pOptionalPreallocatedBuffer;
56171  } else {
56172  size_t bufferSizeInBytes;
56173 
56174  /*
56175  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
56176  we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
56177  */
56178  pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT;
56179 
56180  bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
56181  pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
56182  if (pRB->pBuffer == NULL) {
56183  return MA_OUT_OF_MEMORY;
56184  }
56185 
56186  MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
56187  pRB->ownsBuffer = MA_TRUE;
56188  }
56189 
56190  return MA_SUCCESS;
56191 }
56192 
56193 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
56194 {
56195  return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
56196 }
56197 
56198 MA_API void ma_rb_uninit(ma_rb* pRB)
56199 {
56200  if (pRB == NULL) {
56201  return;
56202  }
56203 
56204  if (pRB->ownsBuffer) {
56205  ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks);
56206  }
56207 }
56208 
56209 MA_API void ma_rb_reset(ma_rb* pRB)
56210 {
56211  if (pRB == NULL) {
56212  return;
56213  }
56214 
56215  ma_atomic_exchange_32(&pRB->encodedReadOffset, 0);
56216  ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0);
56217 }
56218 
56219 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
56220 {
56221  ma_uint32 writeOffset;
56222  ma_uint32 writeOffsetInBytes;
56223  ma_uint32 writeOffsetLoopFlag;
56224  ma_uint32 readOffset;
56225  ma_uint32 readOffsetInBytes;
56226  ma_uint32 readOffsetLoopFlag;
56227  size_t bytesAvailable;
56228  size_t bytesRequested;
56229 
56230  if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
56231  return MA_INVALID_ARGS;
56232  }
56233 
56234  /* The returned buffer should never move ahead of the write pointer. */
56235  writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
56236  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56237 
56238  readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
56239  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56240 
56241  /*
56242  The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
56243  can only read up to the write pointer. If not, we can only read up to the end of the buffer.
56244  */
56245  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
56246  bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
56247  } else {
56248  bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
56249  }
56250 
56251  bytesRequested = *pSizeInBytes;
56252  if (bytesRequested > bytesAvailable) {
56253  bytesRequested = bytesAvailable;
56254  }
56255 
56256  *pSizeInBytes = bytesRequested;
56257  (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
56258 
56259  return MA_SUCCESS;
56260 }
56261 
56262 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes)
56263 {
56264  ma_uint32 readOffset;
56265  ma_uint32 readOffsetInBytes;
56266  ma_uint32 readOffsetLoopFlag;
56267  ma_uint32 newReadOffsetInBytes;
56268  ma_uint32 newReadOffsetLoopFlag;
56269 
56270  if (pRB == NULL) {
56271  return MA_INVALID_ARGS;
56272  }
56273 
56274  readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
56275  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56276 
56277  /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
56278  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
56279  if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
56280  return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
56281  }
56282 
56283  /* Move the read pointer back to the start if necessary. */
56284  newReadOffsetLoopFlag = readOffsetLoopFlag;
56285  if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
56286  newReadOffsetInBytes = 0;
56287  newReadOffsetLoopFlag ^= 0x80000000;
56288  }
56289 
56290  ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
56291 
56292  if (ma_rb_pointer_distance(pRB) == 0) {
56293  return MA_AT_END;
56294  } else {
56295  return MA_SUCCESS;
56296  }
56297 }
56298 
56299 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
56300 {
56301  ma_uint32 readOffset;
56302  ma_uint32 readOffsetInBytes;
56303  ma_uint32 readOffsetLoopFlag;
56304  ma_uint32 writeOffset;
56305  ma_uint32 writeOffsetInBytes;
56306  ma_uint32 writeOffsetLoopFlag;
56307  size_t bytesAvailable;
56308  size_t bytesRequested;
56309 
56310  if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
56311  return MA_INVALID_ARGS;
56312  }
56313 
56314  /* The returned buffer should never overtake the read buffer. */
56315  readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
56316  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56317 
56318  writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
56319  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56320 
56321  /*
56322  In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
56323  write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
56324  never overtake the read pointer.
56325  */
56326  if (writeOffsetLoopFlag == readOffsetLoopFlag) {
56327  bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
56328  } else {
56329  bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
56330  }
56331 
56332  bytesRequested = *pSizeInBytes;
56333  if (bytesRequested > bytesAvailable) {
56334  bytesRequested = bytesAvailable;
56335  }
56336 
56337  *pSizeInBytes = bytesRequested;
56338  *ppBufferOut = ma_rb__get_write_ptr(pRB);
56339 
56340  /* Clear the buffer if desired. */
56341  if (pRB->clearOnWriteAcquire) {
56342  MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
56343  }
56344 
56345  return MA_SUCCESS;
56346 }
56347 
56348 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes)
56349 {
56350  ma_uint32 writeOffset;
56351  ma_uint32 writeOffsetInBytes;
56352  ma_uint32 writeOffsetLoopFlag;
56353  ma_uint32 newWriteOffsetInBytes;
56354  ma_uint32 newWriteOffsetLoopFlag;
56355 
56356  if (pRB == NULL) {
56357  return MA_INVALID_ARGS;
56358  }
56359 
56360  writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
56361  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56362 
56363  /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
56364  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
56365  if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
56366  return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
56367  }
56368 
56369  /* Move the read pointer back to the start if necessary. */
56370  newWriteOffsetLoopFlag = writeOffsetLoopFlag;
56371  if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
56372  newWriteOffsetInBytes = 0;
56373  newWriteOffsetLoopFlag ^= 0x80000000;
56374  }
56375 
56376  ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
56377 
56378  if (ma_rb_pointer_distance(pRB) == 0) {
56379  return MA_AT_END;
56380  } else {
56381  return MA_SUCCESS;
56382  }
56383 }
56384 
56385 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
56386 {
56387  ma_uint32 readOffset;
56388  ma_uint32 readOffsetInBytes;
56389  ma_uint32 readOffsetLoopFlag;
56390  ma_uint32 writeOffset;
56391  ma_uint32 writeOffsetInBytes;
56392  ma_uint32 writeOffsetLoopFlag;
56393  ma_uint32 newReadOffsetInBytes;
56394  ma_uint32 newReadOffsetLoopFlag;
56395 
56396  if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
56397  return MA_INVALID_ARGS;
56398  }
56399 
56400  readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
56401  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56402 
56403  writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
56404  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56405 
56406  newReadOffsetLoopFlag = readOffsetLoopFlag;
56407 
56408  /* We cannot go past the write buffer. */
56409  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
56410  if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
56411  newReadOffsetInBytes = writeOffsetInBytes;
56412  } else {
56413  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
56414  }
56415  } else {
56416  /* May end up looping. */
56417  if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
56418  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
56419  newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
56420  } else {
56421  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
56422  }
56423  }
56424 
56425  ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
56426  return MA_SUCCESS;
56427 }
56428 
56429 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
56430 {
56431  ma_uint32 readOffset;
56432  ma_uint32 readOffsetInBytes;
56433  ma_uint32 readOffsetLoopFlag;
56434  ma_uint32 writeOffset;
56435  ma_uint32 writeOffsetInBytes;
56436  ma_uint32 writeOffsetLoopFlag;
56437  ma_uint32 newWriteOffsetInBytes;
56438  ma_uint32 newWriteOffsetLoopFlag;
56439 
56440  if (pRB == NULL) {
56441  return MA_INVALID_ARGS;
56442  }
56443 
56444  readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
56445  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56446 
56447  writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
56448  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56449 
56450  newWriteOffsetLoopFlag = writeOffsetLoopFlag;
56451 
56452  /* We cannot go past the write buffer. */
56453  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
56454  /* May end up looping. */
56455  if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
56456  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
56457  newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
56458  } else {
56459  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
56460  }
56461  } else {
56462  if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
56463  newWriteOffsetInBytes = readOffsetInBytes;
56464  } else {
56465  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
56466  }
56467  }
56468 
56469  ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
56470  return MA_SUCCESS;
56471 }
56472 
56473 MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB)
56474 {
56475  ma_uint32 readOffset;
56476  ma_uint32 readOffsetInBytes;
56477  ma_uint32 readOffsetLoopFlag;
56478  ma_uint32 writeOffset;
56479  ma_uint32 writeOffsetInBytes;
56480  ma_uint32 writeOffsetLoopFlag;
56481 
56482  if (pRB == NULL) {
56483  return 0;
56484  }
56485 
56486  readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
56487  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56488 
56489  writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
56490  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56491 
56492  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
56493  return writeOffsetInBytes - readOffsetInBytes;
56494  } else {
56495  return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
56496  }
56497 }
56498 
56499 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB)
56500 {
56501  ma_int32 dist;
56502 
56503  if (pRB == NULL) {
56504  return 0;
56505  }
56506 
56507  dist = ma_rb_pointer_distance(pRB);
56508  if (dist < 0) {
56509  return 0;
56510  }
56511 
56512  return dist;
56513 }
56514 
56515 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB)
56516 {
56517  if (pRB == NULL) {
56518  return 0;
56519  }
56520 
56521  return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB));
56522 }
56523 
56524 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
56525 {
56526  if (pRB == NULL) {
56527  return 0;
56528  }
56529 
56530  return pRB->subbufferSizeInBytes;
56531 }
56532 
56533 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
56534 {
56535  if (pRB == NULL) {
56536  return 0;
56537  }
56538 
56539  if (pRB->subbufferStrideInBytes == 0) {
56540  return (size_t)pRB->subbufferSizeInBytes;
56541  }
56542 
56543  return (size_t)pRB->subbufferStrideInBytes;
56544 }
56545 
56546 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
56547 {
56548  if (pRB == NULL) {
56549  return 0;
56550  }
56551 
56552  return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
56553 }
56554 
56555 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
56556 {
56557  if (pRB == NULL) {
56558  return NULL;
56559  }
56560 
56561  return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
56562 }
56563 
56564 
56565 
56566 static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
56567 {
56568  /* 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. */
56569  ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;
56570  ma_result result;
56571  ma_uint64 totalFramesRead;
56572 
56573  MA_ASSERT(pRB != NULL);
56574 
56575  /* We need to run this in a loop since the ring buffer itself may loop. */
56576  totalFramesRead = 0;
56577  while (totalFramesRead < frameCount) {
56578  void* pMappedBuffer;
56579  ma_uint32 mappedFrameCount;
56580  ma_uint64 framesToRead = frameCount - totalFramesRead;
56581  if (framesToRead > 0xFFFFFFFF) {
56582  framesToRead = 0xFFFFFFFF;
56583  }
56584 
56585  mappedFrameCount = (ma_uint32)framesToRead;
56586  result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer);
56587  if (result != MA_SUCCESS) {
56588  break;
56589  }
56590 
56591  if (mappedFrameCount == 0) {
56592  break; /* <-- End of ring buffer. */
56593  }
56594 
56595  ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels);
56596 
56597  result = ma_pcm_rb_commit_read(pRB, mappedFrameCount);
56598  if (result != MA_SUCCESS) {
56599  break;
56600  }
56601 
56602  totalFramesRead += mappedFrameCount;
56603  }
56604 
56605  *pFramesRead = totalFramesRead;
56606  return MA_SUCCESS;
56607 }
56608 
56609 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)
56610 {
56611  ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;
56612  MA_ASSERT(pRB != NULL);
56613 
56614  if (pFormat != NULL) {
56615  *pFormat = pRB->format;
56616  }
56617 
56618  if (pChannels != NULL) {
56619  *pChannels = pRB->channels;
56620  }
56621 
56622  if (pSampleRate != NULL) {
56623  *pSampleRate = pRB->sampleRate;
56624  }
56625 
56626  /* Just assume the default channel map. */
56627  if (pChannelMap != NULL) {
56628  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels);
56629  }
56630 
56631  return MA_SUCCESS;
56632 }
56633 
56634 static ma_data_source_vtable ma_gRBDataSourceVTable =
56635 {
56636  ma_pcm_rb_data_source__on_read,
56637  NULL, /* onSeek */
56638  ma_pcm_rb_data_source__on_get_data_format,
56639  NULL, /* onGetCursor */
56640  NULL, /* onGetLength */
56641  NULL, /* onSetLooping */
56642  0
56643 };
56644 
56645 static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
56646 {
56647  MA_ASSERT(pRB != NULL);
56648 
56649  return ma_get_bytes_per_frame(pRB->format, pRB->channels);
56650 }
56651 
56652 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)
56653 {
56654  ma_uint32 bpf;
56655  ma_result result;
56656 
56657  if (pRB == NULL) {
56658  return MA_INVALID_ARGS;
56659  }
56660 
56661  MA_ZERO_OBJECT(pRB);
56662 
56663  bpf = ma_get_bytes_per_frame(format, channels);
56664  if (bpf == 0) {
56665  return MA_INVALID_ARGS;
56666  }
56667 
56668  result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
56669  if (result != MA_SUCCESS) {
56670  return result;
56671  }
56672 
56673  pRB->format = format;
56674  pRB->channels = channels;
56675  pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */
56676 
56677  /* The PCM ring buffer is a data source. We need to get that set up as well. */
56678  {
56679  ma_data_source_config dataSourceConfig = ma_data_source_config_init();
56680  dataSourceConfig.vtable = &ma_gRBDataSourceVTable;
56681 
56682  result = ma_data_source_init(&dataSourceConfig, &pRB->ds);
56683  if (result != MA_SUCCESS) {
56684  ma_rb_uninit(&pRB->rb);
56685  return result;
56686  }
56687  }
56688 
56689  return MA_SUCCESS;
56690 }
56691 
56692 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)
56693 {
56694  return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
56695 }
56696 
56697 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
56698 {
56699  if (pRB == NULL) {
56700  return;
56701  }
56702 
56703  ma_data_source_uninit(&pRB->ds);
56704  ma_rb_uninit(&pRB->rb);
56705 }
56706 
56707 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB)
56708 {
56709  if (pRB == NULL) {
56710  return;
56711  }
56712 
56713  ma_rb_reset(&pRB->rb);
56714 }
56715 
56716 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
56717 {
56718  size_t sizeInBytes;
56719  ma_result result;
56720 
56721  if (pRB == NULL || pSizeInFrames == NULL) {
56722  return MA_INVALID_ARGS;
56723  }
56724 
56725  sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
56726 
56727  result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
56728  if (result != MA_SUCCESS) {
56729  return result;
56730  }
56731 
56732  *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
56733  return MA_SUCCESS;
56734 }
56735 
56736 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)
56737 {
56738  if (pRB == NULL) {
56739  return MA_INVALID_ARGS;
56740  }
56741 
56742  return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
56743 }
56744 
56745 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
56746 {
56747  size_t sizeInBytes;
56748  ma_result result;
56749 
56750  if (pRB == NULL) {
56751  return MA_INVALID_ARGS;
56752  }
56753 
56754  sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
56755 
56756  result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
56757  if (result != MA_SUCCESS) {
56758  return result;
56759  }
56760 
56761  *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
56762  return MA_SUCCESS;
56763 }
56764 
56765 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)
56766 {
56767  if (pRB == NULL) {
56768  return MA_INVALID_ARGS;
56769  }
56770 
56771  return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
56772 }
56773 
56774 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
56775 {
56776  if (pRB == NULL) {
56777  return MA_INVALID_ARGS;
56778  }
56779 
56780  return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
56781 }
56782 
56783 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
56784 {
56785  if (pRB == NULL) {
56786  return MA_INVALID_ARGS;
56787  }
56788 
56789  return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
56790 }
56791 
56792 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB)
56793 {
56794  if (pRB == NULL) {
56795  return 0;
56796  }
56797 
56798  return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
56799 }
56800 
56801 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB)
56802 {
56803  if (pRB == NULL) {
56804  return 0;
56805  }
56806 
56807  return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
56808 }
56809 
56810 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB)
56811 {
56812  if (pRB == NULL) {
56813  return 0;
56814  }
56815 
56816  return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
56817 }
56818 
56819 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB)
56820 {
56821  if (pRB == NULL) {
56822  return 0;
56823  }
56824 
56825  return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
56826 }
56827 
56828 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB)
56829 {
56830  if (pRB == NULL) {
56831  return 0;
56832  }
56833 
56834  return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
56835 }
56836 
56837 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)
56838 {
56839  if (pRB == NULL) {
56840  return 0;
56841  }
56842 
56843  return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
56844 }
56845 
56846 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
56847 {
56848  if (pRB == NULL) {
56849  return NULL;
56850  }
56851 
56852  return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
56853 }
56854 
56855 MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB)
56856 {
56857  if (pRB == NULL) {
56858  return ma_format_unknown;
56859  }
56860 
56861  return pRB->format;
56862 }
56863 
56864 MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB)
56865 {
56866  if (pRB == NULL) {
56867  return 0;
56868  }
56869 
56870  return pRB->channels;
56871 }
56872 
56873 MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB)
56874 {
56875  if (pRB == NULL) {
56876  return 0;
56877  }
56878 
56879  return pRB->sampleRate;
56880 }
56881 
56882 MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate)
56883 {
56884  if (pRB == NULL) {
56885  return;
56886  }
56887 
56888  pRB->sampleRate = sampleRate;
56889 }
56890 
56891 
56892 
56893 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)
56894 {
56895  ma_result result;
56896  ma_uint32 sizeInFrames;
56897 
56898  sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
56899  if (sizeInFrames == 0) {
56900  return MA_INVALID_ARGS;
56901  }
56902 
56903  result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
56904  if (result != MA_SUCCESS) {
56905  return result;
56906  }
56907 
56908  /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */
56909  ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
56910 
56911  return MA_SUCCESS;
56912 }
56913 
56914 MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)
56915 {
56916  ma_pcm_rb_uninit((ma_pcm_rb*)pRB);
56917  return MA_SUCCESS;
56918 }
56919 
56920 
56921 
56922 /**************************************************************************************************************************************************************
56923 
56924 Miscellaneous Helpers
56925 
56926 **************************************************************************************************************************************************************/
56927 MA_API const char* ma_result_description(ma_result result)
56928 {
56929  switch (result)
56930  {
56931  case MA_SUCCESS: return "No error";
56932  case MA_ERROR: return "Unknown error";
56933  case MA_INVALID_ARGS: return "Invalid argument";
56934  case MA_INVALID_OPERATION: return "Invalid operation";
56935  case MA_OUT_OF_MEMORY: return "Out of memory";
56936  case MA_OUT_OF_RANGE: return "Out of range";
56937  case MA_ACCESS_DENIED: return "Permission denied";
56938  case MA_DOES_NOT_EXIST: return "Resource does not exist";
56939  case MA_ALREADY_EXISTS: return "Resource already exists";
56940  case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
56941  case MA_INVALID_FILE: return "Invalid file";
56942  case MA_TOO_BIG: return "Too large";
56943  case MA_PATH_TOO_LONG: return "Path too long";
56944  case MA_NAME_TOO_LONG: return "Name too long";
56945  case MA_NOT_DIRECTORY: return "Not a directory";
56946  case MA_IS_DIRECTORY: return "Is a directory";
56947  case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
56948  case MA_AT_END: return "At end";
56949  case MA_NO_SPACE: return "No space available";
56950  case MA_BUSY: return "Device or resource busy";
56951  case MA_IO_ERROR: return "Input/output error";
56952  case MA_INTERRUPT: return "Interrupted";
56953  case MA_UNAVAILABLE: return "Resource unavailable";
56954  case MA_ALREADY_IN_USE: return "Resource already in use";
56955  case MA_BAD_ADDRESS: return "Bad address";
56956  case MA_BAD_SEEK: return "Illegal seek";
56957  case MA_BAD_PIPE: return "Broken pipe";
56958  case MA_DEADLOCK: return "Deadlock";
56959  case MA_TOO_MANY_LINKS: return "Too many links";
56960  case MA_NOT_IMPLEMENTED: return "Not implemented";
56961  case MA_NO_MESSAGE: return "No message of desired type";
56962  case MA_BAD_MESSAGE: return "Invalid message";
56963  case MA_NO_DATA_AVAILABLE: return "No data available";
56964  case MA_INVALID_DATA: return "Invalid data";
56965  case MA_TIMEOUT: return "Timeout";
56966  case MA_NO_NETWORK: return "Network unavailable";
56967  case MA_NOT_UNIQUE: return "Not unique";
56968  case MA_NOT_SOCKET: return "Socket operation on non-socket";
56969  case MA_NO_ADDRESS: return "Destination address required";
56970  case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
56971  case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
56972  case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
56973  case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
56974  case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
56975  case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
56976  case MA_CONNECTION_RESET: return "Connection reset";
56977  case MA_ALREADY_CONNECTED: return "Already connected";
56978  case MA_NOT_CONNECTED: return "Not connected";
56979  case MA_CONNECTION_REFUSED: return "Connection refused";
56980  case MA_NO_HOST: return "No host";
56981  case MA_IN_PROGRESS: return "Operation in progress";
56982  case MA_CANCELLED: return "Operation cancelled";
56983  case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
56984 
56985  case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
56986  case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
56987  case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
56988  case MA_NO_BACKEND: return "No backend";
56989  case MA_NO_DEVICE: return "No device";
56990  case MA_API_NOT_FOUND: return "API not found";
56991  case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
56992 
56993  case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
56994  case MA_DEVICE_NOT_STARTED: return "Device not started";
56995 
56996  case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
56997  case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
56998  case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
56999  case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
57000 
57001  default: return "Unknown error";
57002  }
57003 }
57004 
57005 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
57006 {
57007  if (pAllocationCallbacks != NULL) {
57008  if (pAllocationCallbacks->onMalloc != NULL) {
57009  return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
57010  } else {
57011  return NULL; /* Do not fall back to the default implementation. */
57012  }
57013  } else {
57014  return ma__malloc_default(sz, NULL);
57015  }
57016 }
57017 
57018 MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
57019 {
57020  void* p = ma_malloc(sz, pAllocationCallbacks);
57021  if (p != NULL) {
57022  MA_ZERO_MEMORY(p, sz);
57023  }
57024 
57025  return p;
57026 }
57027 
57028 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
57029 {
57030  if (pAllocationCallbacks != NULL) {
57031  if (pAllocationCallbacks->onRealloc != NULL) {
57032  return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
57033  } else {
57034  return NULL; /* Do not fall back to the default implementation. */
57035  }
57036  } else {
57037  return ma__realloc_default(p, sz, NULL);
57038  }
57039 }
57040 
57041 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
57042 {
57043  if (p == NULL) {
57044  return;
57045  }
57046 
57047  if (pAllocationCallbacks != NULL) {
57048  if (pAllocationCallbacks->onFree != NULL) {
57049  pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
57050  } else {
57051  return; /* Do no fall back to the default implementation. */
57052  }
57053  } else {
57054  ma__free_default(p, NULL);
57055  }
57056 }
57057 
57058 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
57059 {
57060  size_t extraBytes;
57061  void* pUnaligned;
57062  void* pAligned;
57063 
57064  if (alignment == 0) {
57065  return 0;
57066  }
57067 
57068  extraBytes = alignment-1 + sizeof(void*);
57069 
57070  pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
57071  if (pUnaligned == NULL) {
57072  return NULL;
57073  }
57074 
57075  pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
57076  ((void**)pAligned)[-1] = pUnaligned;
57077 
57078  return pAligned;
57079 }
57080 
57081 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
57082 {
57083  ma_free(((void**)p)[-1], pAllocationCallbacks);
57084 }
57085 
57086 MA_API const char* ma_get_format_name(ma_format format)
57087 {
57088  switch (format)
57089  {
57090  case ma_format_unknown: return "Unknown";
57091  case ma_format_u8: return "8-bit Unsigned Integer";
57092  case ma_format_s16: return "16-bit Signed Integer";
57093  case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
57094  case ma_format_s32: return "32-bit Signed Integer";
57095  case ma_format_f32: return "32-bit IEEE Floating Point";
57096  default: return "Invalid";
57097  }
57098 }
57099 
57100 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
57101 {
57102  ma_uint32 i;
57103  for (i = 0; i < channels; ++i) {
57104  pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
57105  }
57106 }
57107 
57108 
57109 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)
57110 {
57111  ma_uint32 sizes[] = {
57112  0, /* unknown */
57113  1, /* u8 */
57114  2, /* s16 */
57115  3, /* s24 */
57116  4, /* s32 */
57117  4, /* f32 */
57118  };
57119  return sizes[format];
57120 }
57121 
57122 
57123 
57124 #define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0
57125 #define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0)
57126 #define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0
57127 #define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0)
57128 
57129 MA_API ma_data_source_config ma_data_source_config_init(void)
57130 {
57131  ma_data_source_config config;
57132 
57133  MA_ZERO_OBJECT(&config);
57134 
57135  return config;
57136 }
57137 
57138 
57139 MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource)
57140 {
57141  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57142 
57143  if (pDataSource == NULL) {
57144  return MA_INVALID_ARGS;
57145  }
57146 
57147  MA_ZERO_OBJECT(pDataSourceBase);
57148 
57149  if (pConfig == NULL) {
57150  return MA_INVALID_ARGS;
57151  }
57152 
57153  pDataSourceBase->vtable = pConfig->vtable;
57154  pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
57155  pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
57156  pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;
57157  pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;
57158  pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */
57159  pDataSourceBase->pNext = NULL;
57160  pDataSourceBase->onGetNext = NULL;
57161 
57162  return MA_SUCCESS;
57163 }
57164 
57165 MA_API void ma_data_source_uninit(ma_data_source* pDataSource)
57166 {
57167  if (pDataSource == NULL) {
57168  return;
57169  }
57170 
57171  /*
57172  This is placeholder in case we need this later. Data sources need to call this in their
57173  uninitialization routine to ensure things work later on if something is added here.
57174  */
57175 }
57176 
57177 static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource)
57178 {
57179  ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource;
57180 
57181  MA_ASSERT(pDataSource != NULL);
57182  MA_ASSERT(ppCurrentDataSource != NULL);
57183 
57184  if (pCurrentDataSource->pCurrent == NULL) {
57185  /*
57186  The current data source is NULL. If we're using this in the context of a chain we need to return NULL
57187  here so that we don't end up looping. Otherwise we just return the data source itself.
57188  */
57189  if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) {
57190  pCurrentDataSource = NULL;
57191  } else {
57192  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. */
57193  }
57194  } else {
57195  pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent;
57196  }
57197 
57198  *ppCurrentDataSource = pCurrentDataSource;
57199 
57200  return MA_SUCCESS;
57201 }
57202 
57203 static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
57204 {
57205  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57206  ma_result result;
57207  ma_uint64 framesRead = 0;
57208  ma_bool32 loop = ma_data_source_is_looping(pDataSource);
57209 
57210  if (pDataSourceBase == NULL) {
57211  return MA_AT_END;
57212  }
57213 
57214  if (frameCount == 0) {
57215  return MA_INVALID_ARGS;
57216  }
57217 
57218  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))) {
57219  /* 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. */
57220  result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
57221  } else {
57222  /* Need to clamp to within the range. */
57223  ma_uint64 relativeCursor;
57224  ma_uint64 absoluteCursor;
57225 
57226  result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor);
57227  if (result != MA_SUCCESS) {
57228  /* 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. */
57229  result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
57230  } else {
57231  ma_uint64 rangeBeg;
57232  ma_uint64 rangeEnd;
57233 
57234  /* We have the cursor. We need to make sure we don't read beyond our range. */
57235  rangeBeg = pDataSourceBase->rangeBegInFrames;
57236  rangeEnd = pDataSourceBase->rangeEndInFrames;
57237 
57238  absoluteCursor = rangeBeg + relativeCursor;
57239 
57240  /* If looping, make sure we're within range. */
57241  if (loop) {
57242  if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
57243  rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames);
57244  }
57245  }
57246 
57247  if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) {
57248  frameCount = (rangeEnd - absoluteCursor);
57249  }
57250 
57251  /*
57252  If the cursor is sitting on the end of the range the frame count will be set to 0 which can
57253  result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return
57254  MA_AT_END so the higher level function can know about it.
57255  */
57256  if (frameCount > 0) {
57257  result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
57258  } else {
57259  result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */
57260  }
57261  }
57262  }
57263 
57264  if (pFramesRead != NULL) {
57265  *pFramesRead = framesRead;
57266  }
57267 
57268  /* We need to make sure MA_AT_END is returned if we hit the end of the range. */
57269  if (result == MA_SUCCESS && framesRead == 0) {
57270  result = MA_AT_END;
57271  }
57272 
57273  return result;
57274 }
57275 
57276 MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
57277 {
57278  ma_result result = MA_SUCCESS;
57279  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57280  ma_data_source_base* pCurrentDataSource;
57281  void* pRunningFramesOut = pFramesOut;
57282  ma_uint64 totalFramesProcessed = 0;
57283  ma_format format;
57284  ma_uint32 channels;
57285  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. */
57286  ma_bool32 loop;
57287 
57288  if (pFramesRead != NULL) {
57289  *pFramesRead = 0;
57290  }
57291 
57292  if (frameCount == 0) {
57293  return MA_INVALID_ARGS;
57294  }
57295 
57296  if (pDataSourceBase == NULL) {
57297  return MA_INVALID_ARGS;
57298  }
57299 
57300  loop = ma_data_source_is_looping(pDataSource);
57301 
57302  /*
57303  We need to know the data format so we can advance the output buffer as we read frames. If this
57304  fails, chaining will not work and we'll just read as much as we can from the current source.
57305  */
57306  if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) {
57307  result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
57308  if (result != MA_SUCCESS) {
57309  return result;
57310  }
57311 
57312  return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead);
57313  }
57314 
57315  /*
57316  Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and
57317  only the current data source will be read from.
57318  */
57319 
57320  /* Keep reading until we've read as many frames as possible. */
57321  while (totalFramesProcessed < frameCount) {
57322  ma_uint64 framesProcessed;
57323  ma_uint64 framesRemaining = frameCount - totalFramesProcessed;
57324 
57325  /* We need to resolve the data source that we'll actually be reading from. */
57326  result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
57327  if (result != MA_SUCCESS) {
57328  break;
57329  }
57330 
57331  if (pCurrentDataSource == NULL) {
57332  break;
57333  }
57334 
57335  result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);
57336  totalFramesProcessed += framesProcessed;
57337 
57338  /*
57339  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
57340  not necessarily considered an error.
57341  */
57342  if (result != MA_SUCCESS && result != MA_AT_END) {
57343  break;
57344  }
57345 
57346  /*
57347  We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned
57348  MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame.
57349  */
57350  if (result == MA_AT_END) {
57351  /*
57352  The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't
57353  accidentally return MA_AT_END when data has been read in prior loop iterations. at the
57354  end of this function, the result will be checked for MA_SUCCESS, and if the total
57355  number of frames processed is 0, will be explicitly set to MA_AT_END.
57356  */
57357  result = MA_SUCCESS;
57358 
57359  /*
57360  We reached the end. If we're looping, we just loop back to the start of the current
57361  data source. If we're not looping we need to check if we have another in the chain, and
57362  if so, switch to it.
57363  */
57364  if (loop) {
57365  if (framesProcessed == 0) {
57366  emptyLoopCounter += 1;
57367  if (emptyLoopCounter > 1) {
57368  break; /* Infinite loop detected. Get out. */
57369  }
57370  } else {
57371  emptyLoopCounter = 0;
57372  }
57373 
57374  result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames);
57375  if (result != MA_SUCCESS) {
57376  break; /* Failed to loop. Abort. */
57377  }
57378 
57379  /* Don't return MA_AT_END for looping sounds. */
57380  result = MA_SUCCESS;
57381  } else {
57382  if (pCurrentDataSource->pNext != NULL) {
57383  pDataSourceBase->pCurrent = pCurrentDataSource->pNext;
57384  } else if (pCurrentDataSource->onGetNext != NULL) {
57385  pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource);
57386  if (pDataSourceBase->pCurrent == NULL) {
57387  break; /* Our callback did not return a next data source. We're done. */
57388  }
57389  } else {
57390  /* Reached the end of the chain. We're done. */
57391  break;
57392  }
57393 
57394  /* The next data source needs to be rewound to ensure data is read in looping scenarios. */
57395  result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0);
57396  if (result != MA_SUCCESS) {
57397  break;
57398  }
57399  }
57400  }
57401 
57402  if (pRunningFramesOut != NULL) {
57403  pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));
57404  }
57405  }
57406 
57407  if (pFramesRead != NULL) {
57408  *pFramesRead = totalFramesProcessed;
57409  }
57410 
57411  MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */
57412 
57413  if (result == MA_SUCCESS && totalFramesProcessed == 0) {
57414  result = MA_AT_END;
57415  }
57416 
57417  return result;
57418 }
57419 
57420 MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked)
57421 {
57422  return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked);
57423 }
57424 
57425 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
57426 {
57427  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57428 
57429  if (pDataSourceBase == NULL) {
57430  return MA_SUCCESS;
57431  }
57432 
57433  if (pDataSourceBase->vtable->onSeek == NULL) {
57434  return MA_NOT_IMPLEMENTED;
57435  }
57436 
57437  if (frameIndex > pDataSourceBase->rangeEndInFrames) {
57438  return MA_INVALID_OPERATION; /* Trying to seek to far forward. */
57439  }
57440 
57441  return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex);
57442 }
57443 
57444 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)
57445 {
57446  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57447  ma_result result;
57448  ma_format format;
57449  ma_uint32 channels;
57450  ma_uint32 sampleRate;
57451 
57452  /* Initialize to defaults for safety just in case the data source does not implement this callback. */
57453  if (pFormat != NULL) {
57454  *pFormat = ma_format_unknown;
57455  }
57456  if (pChannels != NULL) {
57457  *pChannels = 0;
57458  }
57459  if (pSampleRate != NULL) {
57460  *pSampleRate = 0;
57461  }
57462  if (pChannelMap != NULL) {
57463  MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
57464  }
57465 
57466  if (pDataSourceBase == NULL) {
57467  return MA_INVALID_ARGS;
57468  }
57469 
57470  if (pDataSourceBase->vtable->onGetDataFormat == NULL) {
57471  return MA_NOT_IMPLEMENTED;
57472  }
57473 
57474  result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap);
57475  if (result != MA_SUCCESS) {
57476  return result;
57477  }
57478 
57479  if (pFormat != NULL) {
57480  *pFormat = format;
57481  }
57482  if (pChannels != NULL) {
57483  *pChannels = channels;
57484  }
57485  if (pSampleRate != NULL) {
57486  *pSampleRate = sampleRate;
57487  }
57488 
57489  /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */
57490 
57491  return MA_SUCCESS;
57492 }
57493 
57494 MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
57495 {
57496  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57497  ma_result result;
57498  ma_uint64 cursor;
57499 
57500  if (pCursor == NULL) {
57501  return MA_INVALID_ARGS;
57502  }
57503 
57504  *pCursor = 0;
57505 
57506  if (pDataSourceBase == NULL) {
57507  return MA_SUCCESS;
57508  }
57509 
57510  if (pDataSourceBase->vtable->onGetCursor == NULL) {
57511  return MA_NOT_IMPLEMENTED;
57512  }
57513 
57514  result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor);
57515  if (result != MA_SUCCESS) {
57516  return result;
57517  }
57518 
57519  /* The cursor needs to be made relative to the start of the range. */
57520  if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */
57521  *pCursor = 0;
57522  } else {
57523  *pCursor = cursor - pDataSourceBase->rangeBegInFrames;
57524  }
57525 
57526  return MA_SUCCESS;
57527 }
57528 
57529 MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
57530 {
57531  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57532 
57533  if (pLength == NULL) {
57534  return MA_INVALID_ARGS;
57535  }
57536 
57537  *pLength = 0;
57538 
57539  if (pDataSourceBase == NULL) {
57540  return MA_INVALID_ARGS;
57541  }
57542 
57543  /*
57544  If we have a range defined we'll use that to determine the length. This is one of rare times
57545  where we'll actually trust the caller. If they've set the range, I think it's mostly safe to
57546  assume they've set it based on some higher level knowledge of the structure of the sound bank.
57547  */
57548  if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) {
57549  *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames;
57550  return MA_SUCCESS;
57551  }
57552 
57553  /*
57554  Getting here means a range is not defined so we'll need to get the data source itself to tell
57555  us the length.
57556  */
57557  if (pDataSourceBase->vtable->onGetLength == NULL) {
57558  return MA_NOT_IMPLEMENTED;
57559  }
57560 
57561  return pDataSourceBase->vtable->onGetLength(pDataSource, pLength);
57562 }
57563 
57564 MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor)
57565 {
57566  ma_result result;
57567  ma_uint64 cursorInPCMFrames;
57568  ma_uint32 sampleRate;
57569 
57570  if (pCursor == NULL) {
57571  return MA_INVALID_ARGS;
57572  }
57573 
57574  *pCursor = 0;
57575 
57576  result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames);
57577  if (result != MA_SUCCESS) {
57578  return result;
57579  }
57580 
57581  result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
57582  if (result != MA_SUCCESS) {
57583  return result;
57584  }
57585 
57586  /* 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. */
57587  *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;
57588 
57589  return MA_SUCCESS;
57590 }
57591 
57592 MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength)
57593 {
57594  ma_result result;
57595  ma_uint64 lengthInPCMFrames;
57596  ma_uint32 sampleRate;
57597 
57598  if (pLength == NULL) {
57599  return MA_INVALID_ARGS;
57600  }
57601 
57602  *pLength = 0;
57603 
57604  result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames);
57605  if (result != MA_SUCCESS) {
57606  return result;
57607  }
57608 
57609  result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
57610  if (result != MA_SUCCESS) {
57611  return result;
57612  }
57613 
57614  /* 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. */
57615  *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate;
57616 
57617  return MA_SUCCESS;
57618 }
57619 
57620 MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
57621 {
57622  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57623 
57624  if (pDataSource == NULL) {
57625  return MA_INVALID_ARGS;
57626  }
57627 
57628  ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping);
57629 
57630  /* If there's no callback for this just treat it as a successful no-op. */
57631  if (pDataSourceBase->vtable->onSetLooping == NULL) {
57632  return MA_SUCCESS;
57633  }
57634 
57635  return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping);
57636 }
57637 
57638 MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource)
57639 {
57640  const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57641 
57642  if (pDataSource == NULL) {
57643  return MA_FALSE;
57644  }
57645 
57646  return ma_atomic_load_32(&pDataSourceBase->isLooping);
57647 }
57648 
57649 MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
57650 {
57651  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57652  ma_result result;
57653  ma_uint64 relativeCursor;
57654  ma_uint64 absoluteCursor;
57655  ma_bool32 doSeekAdjustment = MA_FALSE;
57656 
57657  if (pDataSource == NULL) {
57658  return MA_INVALID_ARGS;
57659  }
57660 
57661  if (rangeEndInFrames < rangeBegInFrames) {
57662  return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */
57663  }
57664 
57665  /*
57666  We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now
57667  so we can calculate it's absolute position before we change the range.
57668  */
57669  result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor);
57670  if (result == MA_SUCCESS) {
57671  doSeekAdjustment = MA_TRUE;
57672  absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames;
57673  } else {
57674  /*
57675  We couldn't get the position of the cursor. It probably means the data source has no notion
57676  of a cursor. We'll just leave it at position 0. Don't treat this as an error.
57677  */
57678  doSeekAdjustment = MA_FALSE;
57679  relativeCursor = 0;
57680  absoluteCursor = 0;
57681  }
57682 
57683  pDataSourceBase->rangeBegInFrames = rangeBegInFrames;
57684  pDataSourceBase->rangeEndInFrames = rangeEndInFrames;
57685 
57686  /*
57687  The commented out logic below was intended to maintain loop points in response to a change in the
57688  range. However, this is not useful because it results in the sound breaking when you move the range
57689  outside of the old loop points. I'm simplifying this by simply resetting the loop points. The
57690  caller is expected to update their loop points if they change the range.
57691 
57692  In practice this should be mostly a non-issue because the majority of the time the range will be
57693  set once right after initialization.
57694  */
57695  pDataSourceBase->loopBegInFrames = 0;
57696  pDataSourceBase->loopEndInFrames = ~((ma_uint64)0);
57697 
57698 
57699  /*
57700  Seek to within range. Note that our seek positions here are relative to the new range. We don't want
57701  do do this if we failed to retrieve the cursor earlier on because it probably means the data source
57702  has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but
57703  I'm just not even going to attempt it.
57704  */
57705  if (doSeekAdjustment) {
57706  if (absoluteCursor < rangeBegInFrames) {
57707  ma_data_source_seek_to_pcm_frame(pDataSource, 0);
57708  } else if (absoluteCursor > rangeEndInFrames) {
57709  ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);
57710  }
57711  }
57712 
57713  return MA_SUCCESS;
57714 }
57715 
57716 MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
57717 {
57718  const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57719 
57720  if (pDataSource == NULL) {
57721  return;
57722  }
57723 
57724  if (pRangeBegInFrames != NULL) {
57725  *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;
57726  }
57727 
57728  if (pRangeEndInFrames != NULL) {
57729  *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames;
57730  }
57731 }
57732 
57733 MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames)
57734 {
57735  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57736 
57737  if (pDataSource == NULL) {
57738  return MA_INVALID_ARGS;
57739  }
57740 
57741  if (loopEndInFrames < loopBegInFrames) {
57742  return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */
57743  }
57744 
57745  if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) {
57746  return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */
57747  }
57748 
57749  pDataSourceBase->loopBegInFrames = loopBegInFrames;
57750  pDataSourceBase->loopEndInFrames = loopEndInFrames;
57751 
57752  /* The end cannot exceed the range. */
57753  if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
57754  pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames);
57755  }
57756 
57757  return MA_SUCCESS;
57758 }
57759 
57760 MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
57761 {
57762  const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57763 
57764  if (pDataSource == NULL) {
57765  return;
57766  }
57767 
57768  if (pLoopBegInFrames != NULL) {
57769  *pLoopBegInFrames = pDataSourceBase->loopBegInFrames;
57770  }
57771 
57772  if (pLoopEndInFrames != NULL) {
57773  *pLoopEndInFrames = pDataSourceBase->loopEndInFrames;
57774  }
57775 }
57776 
57777 MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource)
57778 {
57779  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57780 
57781  if (pDataSource == NULL) {
57782  return MA_INVALID_ARGS;
57783  }
57784 
57785  pDataSourceBase->pCurrent = pCurrentDataSource;
57786 
57787  return MA_SUCCESS;
57788 }
57789 
57790 MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource)
57791 {
57792  const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57793 
57794  if (pDataSource == NULL) {
57795  return NULL;
57796  }
57797 
57798  return pDataSourceBase->pCurrent;
57799 }
57800 
57801 MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource)
57802 {
57803  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57804 
57805  if (pDataSource == NULL) {
57806  return MA_INVALID_ARGS;
57807  }
57808 
57809  pDataSourceBase->pNext = pNextDataSource;
57810 
57811  return MA_SUCCESS;
57812 }
57813 
57814 MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource)
57815 {
57816  const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57817 
57818  if (pDataSource == NULL) {
57819  return NULL;
57820  }
57821 
57822  return pDataSourceBase->pNext;
57823 }
57824 
57825 MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext)
57826 {
57827  ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57828 
57829  if (pDataSource == NULL) {
57830  return MA_INVALID_ARGS;
57831  }
57832 
57833  pDataSourceBase->onGetNext = onGetNext;
57834 
57835  return MA_SUCCESS;
57836 }
57837 
57838 MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource)
57839 {
57840  const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57841 
57842  if (pDataSource == NULL) {
57843  return NULL;
57844  }
57845 
57846  return pDataSourceBase->onGetNext;
57847 }
57848 
57849 
57850 static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
57851 {
57852  ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
57853  ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE);
57854 
57855  if (pFramesRead != NULL) {
57856  *pFramesRead = framesRead;
57857  }
57858 
57859  if (framesRead < frameCount || framesRead == 0) {
57860  return MA_AT_END;
57861  }
57862 
57863  return MA_SUCCESS;
57864 }
57865 
57866 static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
57867 {
57868  return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex);
57869 }
57870 
57871 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)
57872 {
57873  ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
57874 
57875  *pFormat = pAudioBufferRef->format;
57876  *pChannels = pAudioBufferRef->channels;
57877  *pSampleRate = pAudioBufferRef->sampleRate;
57878  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels);
57879 
57880  return MA_SUCCESS;
57881 }
57882 
57883 static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
57884 {
57885  ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
57886 
57887  *pCursor = pAudioBufferRef->cursor;
57888 
57889  return MA_SUCCESS;
57890 }
57891 
57892 static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
57893 {
57894  ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
57895 
57896  *pLength = pAudioBufferRef->sizeInFrames;
57897 
57898  return MA_SUCCESS;
57899 }
57900 
57901 static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable =
57902 {
57903  ma_audio_buffer_ref__data_source_on_read,
57904  ma_audio_buffer_ref__data_source_on_seek,
57905  ma_audio_buffer_ref__data_source_on_get_data_format,
57906  ma_audio_buffer_ref__data_source_on_get_cursor,
57907  ma_audio_buffer_ref__data_source_on_get_length,
57908  NULL, /* onSetLooping */
57909  0
57910 };
57911 
57912 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)
57913 {
57914  ma_result result;
57915  ma_data_source_config dataSourceConfig;
57916 
57917  if (pAudioBufferRef == NULL) {
57918  return MA_INVALID_ARGS;
57919  }
57920 
57921  MA_ZERO_OBJECT(pAudioBufferRef);
57922 
57923  dataSourceConfig = ma_data_source_config_init();
57924  dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable;
57925 
57926  result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds);
57927  if (result != MA_SUCCESS) {
57928  return result;
57929  }
57930 
57931  pAudioBufferRef->format = format;
57932  pAudioBufferRef->channels = channels;
57933  pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */
57934  pAudioBufferRef->cursor = 0;
57935  pAudioBufferRef->sizeInFrames = sizeInFrames;
57936  pAudioBufferRef->pData = pData;
57937 
57938  return MA_SUCCESS;
57939 }
57940 
57941 MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef)
57942 {
57943  if (pAudioBufferRef == NULL) {
57944  return;
57945  }
57946 
57947  ma_data_source_uninit(&pAudioBufferRef->ds);
57948 }
57949 
57950 MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames)
57951 {
57952  if (pAudioBufferRef == NULL) {
57953  return MA_INVALID_ARGS;
57954  }
57955 
57956  pAudioBufferRef->cursor = 0;
57957  pAudioBufferRef->sizeInFrames = sizeInFrames;
57958  pAudioBufferRef->pData = pData;
57959 
57960  return MA_SUCCESS;
57961 }
57962 
57963 MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
57964 {
57965  ma_uint64 totalFramesRead = 0;
57966 
57967  if (pAudioBufferRef == NULL) {
57968  return 0;
57969  }
57970 
57971  if (frameCount == 0) {
57972  return 0;
57973  }
57974 
57975  while (totalFramesRead < frameCount) {
57976  ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
57977  ma_uint64 framesRemaining = frameCount - totalFramesRead;
57978  ma_uint64 framesToRead;
57979 
57980  framesToRead = framesRemaining;
57981  if (framesToRead > framesAvailable) {
57982  framesToRead = framesAvailable;
57983  }
57984 
57985  if (pFramesOut != NULL) {
57986  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);
57987  }
57988 
57989  totalFramesRead += framesToRead;
57990 
57991  pAudioBufferRef->cursor += framesToRead;
57992  if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
57993  if (loop) {
57994  pAudioBufferRef->cursor = 0;
57995  } else {
57996  break; /* We've reached the end and we're not looping. Done. */
57997  }
57998  }
57999 
58000  MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames);
58001  }
58002 
58003  return totalFramesRead;
58004 }
58005 
58006 MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex)
58007 {
58008  if (pAudioBufferRef == NULL) {
58009  return MA_INVALID_ARGS;
58010  }
58011 
58012  if (frameIndex > pAudioBufferRef->sizeInFrames) {
58013  return MA_INVALID_ARGS;
58014  }
58015 
58016  pAudioBufferRef->cursor = (size_t)frameIndex;
58017 
58018  return MA_SUCCESS;
58019 }
58020 
58021 MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount)
58022 {
58023  ma_uint64 framesAvailable;
58024  ma_uint64 frameCount = 0;
58025 
58026  if (ppFramesOut != NULL) {
58027  *ppFramesOut = NULL; /* Safety. */
58028  }
58029 
58030  if (pFrameCount != NULL) {
58031  frameCount = *pFrameCount;
58032  *pFrameCount = 0; /* Safety. */
58033  }
58034 
58035  if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
58036  return MA_INVALID_ARGS;
58037  }
58038 
58039  framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
58040  if (frameCount > framesAvailable) {
58041  frameCount = framesAvailable;
58042  }
58043 
58044  *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels));
58045  *pFrameCount = frameCount;
58046 
58047  return MA_SUCCESS;
58048 }
58049 
58050 MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount)
58051 {
58052  ma_uint64 framesAvailable;
58053 
58054  if (pAudioBufferRef == NULL) {
58055  return MA_INVALID_ARGS;
58056  }
58057 
58058  framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
58059  if (frameCount > framesAvailable) {
58060  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. */
58061  }
58062 
58063  pAudioBufferRef->cursor += frameCount;
58064 
58065  if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
58066  return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */
58067  } else {
58068  return MA_SUCCESS;
58069  }
58070 }
58071 
58072 MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef)
58073 {
58074  if (pAudioBufferRef == NULL) {
58075  return MA_FALSE;
58076  }
58077 
58078  return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames;
58079 }
58080 
58081 MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor)
58082 {
58083  if (pCursor == NULL) {
58084  return MA_INVALID_ARGS;
58085  }
58086 
58087  *pCursor = 0;
58088 
58089  if (pAudioBufferRef == NULL) {
58090  return MA_INVALID_ARGS;
58091  }
58092 
58093  *pCursor = pAudioBufferRef->cursor;
58094 
58095  return MA_SUCCESS;
58096 }
58097 
58098 MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength)
58099 {
58100  if (pLength == NULL) {
58101  return MA_INVALID_ARGS;
58102  }
58103 
58104  *pLength = 0;
58105 
58106  if (pAudioBufferRef == NULL) {
58107  return MA_INVALID_ARGS;
58108  }
58109 
58110  *pLength = pAudioBufferRef->sizeInFrames;
58111 
58112  return MA_SUCCESS;
58113 }
58114 
58115 MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames)
58116 {
58117  if (pAvailableFrames == NULL) {
58118  return MA_INVALID_ARGS;
58119  }
58120 
58121  *pAvailableFrames = 0;
58122 
58123  if (pAudioBufferRef == NULL) {
58124  return MA_INVALID_ARGS;
58125  }
58126 
58127  if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) {
58128  *pAvailableFrames = 0;
58129  } else {
58130  *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
58131  }
58132 
58133  return MA_SUCCESS;
58134 }
58135 
58136 
58137 
58138 
58139 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)
58140 {
58141  ma_audio_buffer_config config;
58142 
58143  MA_ZERO_OBJECT(&config);
58144  config.format = format;
58145  config.channels = channels;
58146  config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */
58147  config.sizeInFrames = sizeInFrames;
58148  config.pData = pData;
58149  ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);
58150 
58151  return config;
58152 }
58153 
58154 static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)
58155 {
58156  ma_result result;
58157 
58158  if (pAudioBuffer == NULL) {
58159  return MA_INVALID_ARGS;
58160  }
58161 
58162  MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */
58163 
58164  if (pConfig == NULL) {
58165  return MA_INVALID_ARGS;
58166  }
58167 
58168  if (pConfig->sizeInFrames == 0) {
58169  return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */
58170  }
58171 
58172  result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref);
58173  if (result != MA_SUCCESS) {
58174  return result;
58175  }
58176 
58177  /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */
58178  pAudioBuffer->ref.sampleRate = pConfig->sampleRate;
58179 
58180  ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
58181 
58182  if (doCopy) {
58183  ma_uint64 allocationSizeInBytes;
58184  void* pData;
58185 
58186  allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels);
58187  if (allocationSizeInBytes > MA_SIZE_MAX) {
58188  return MA_OUT_OF_MEMORY; /* Too big. */
58189  }
58190 
58191  pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */
58192  if (pData == NULL) {
58193  return MA_OUT_OF_MEMORY;
58194  }
58195 
58196  if (pConfig->pData != NULL) {
58197  ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
58198  } else {
58199  ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
58200  }
58201 
58202  ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames);
58203  pAudioBuffer->ownsData = MA_TRUE;
58204  } else {
58205  ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames);
58206  pAudioBuffer->ownsData = MA_FALSE;
58207  }
58208 
58209  return MA_SUCCESS;
58210 }
58211 
58212 static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree)
58213 {
58214  if (pAudioBuffer == NULL) {
58215  return;
58216  }
58217 
58218  if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) {
58219  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. */
58220  }
58221 
58222  if (doFree) {
58223  ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks);
58224  }
58225 
58226  ma_audio_buffer_ref_uninit(&pAudioBuffer->ref);
58227 }
58228 
58229 MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
58230 {
58231  return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer);
58232 }
58233 
58234 MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
58235 {
58236  return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer);
58237 }
58238 
58239 MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer)
58240 {
58241  ma_result result;
58242  ma_audio_buffer* pAudioBuffer;
58243  ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */
58244  ma_uint64 allocationSizeInBytes;
58245 
58246  if (ppAudioBuffer == NULL) {
58247  return MA_INVALID_ARGS;
58248  }
58249 
58250  *ppAudioBuffer = NULL; /* Safety. */
58251 
58252  if (pConfig == NULL) {
58253  return MA_INVALID_ARGS;
58254  }
58255 
58256  innerConfig = *pConfig;
58257  ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks);
58258 
58259  allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels));
58260  if (allocationSizeInBytes > MA_SIZE_MAX) {
58261  return MA_OUT_OF_MEMORY; /* Too big. */
58262  }
58263 
58264  pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */
58265  if (pAudioBuffer == NULL) {
58266  return MA_OUT_OF_MEMORY;
58267  }
58268 
58269  if (pConfig->pData != NULL) {
58270  ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
58271  } else {
58272  ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels);
58273  }
58274 
58275  innerConfig.pData = &pAudioBuffer->_pExtraData[0];
58276 
58277  result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer);
58278  if (result != MA_SUCCESS) {
58279  ma_free(pAudioBuffer, &innerConfig.allocationCallbacks);
58280  return result;
58281  }
58282 
58283  *ppAudioBuffer = pAudioBuffer;
58284 
58285  return MA_SUCCESS;
58286 }
58287 
58288 MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer)
58289 {
58290  ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE);
58291 }
58292 
58293 MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer)
58294 {
58295  ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE);
58296 }
58297 
58298 MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
58299 {
58300  if (pAudioBuffer == NULL) {
58301  return 0;
58302  }
58303 
58304  return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop);
58305 }
58306 
58307 MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex)
58308 {
58309  if (pAudioBuffer == NULL) {
58310  return MA_INVALID_ARGS;
58311  }
58312 
58313  return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex);
58314 }
58315 
58316 MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount)
58317 {
58318  if (ppFramesOut != NULL) {
58319  *ppFramesOut = NULL; /* Safety. */
58320  }
58321 
58322  if (pAudioBuffer == NULL) {
58323  if (pFrameCount != NULL) {
58324  *pFrameCount = 0;
58325  }
58326 
58327  return MA_INVALID_ARGS;
58328  }
58329 
58330  return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount);
58331 }
58332 
58333 MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount)
58334 {
58335  if (pAudioBuffer == NULL) {
58336  return MA_INVALID_ARGS;
58337  }
58338 
58339  return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount);
58340 }
58341 
58342 MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer)
58343 {
58344  if (pAudioBuffer == NULL) {
58345  return MA_FALSE;
58346  }
58347 
58348  return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref);
58349 }
58350 
58351 MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor)
58352 {
58353  if (pAudioBuffer == NULL) {
58354  return MA_INVALID_ARGS;
58355  }
58356 
58357  return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor);
58358 }
58359 
58360 MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength)
58361 {
58362  if (pAudioBuffer == NULL) {
58363  return MA_INVALID_ARGS;
58364  }
58365 
58366  return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength);
58367 }
58368 
58369 MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)
58370 {
58371  if (pAvailableFrames == NULL) {
58372  return MA_INVALID_ARGS;
58373  }
58374 
58375  *pAvailableFrames = 0;
58376 
58377  if (pAudioBuffer == NULL) {
58378  return MA_INVALID_ARGS;
58379  }
58380 
58381  return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames);
58382 }
58383 
58384 
58385 
58386 
58387 
58388 MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData)
58389 {
58390  if (pData == NULL) {
58391  return MA_INVALID_ARGS;
58392  }
58393 
58394  MA_ZERO_OBJECT(pData);
58395 
58396  pData->format = format;
58397  pData->channels = channels;
58398  pData->pTail = &pData->head;
58399 
58400  return MA_SUCCESS;
58401 }
58402 
58403 MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks)
58404 {
58406 
58407  if (pData == NULL) {
58408  return;
58409  }
58410 
58411  /* All pages need to be freed. */
58412  pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext);
58413  while (pPage != NULL) {
58414  ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext);
58415 
58416  ma_free(pPage, pAllocationCallbacks);
58417  pPage = pNext;
58418  }
58419 }
58420 
58421 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData)
58422 {
58423  if (pData == NULL) {
58424  return NULL;
58425  }
58426 
58427  return &pData->head;
58428 }
58429 
58430 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData)
58431 {
58432  if (pData == NULL) {
58433  return NULL;
58434  }
58435 
58436  return pData->pTail;
58437 }
58438 
58439 MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength)
58440 {
58442 
58443  if (pLength == NULL) {
58444  return MA_INVALID_ARGS;
58445  }
58446 
58447  *pLength = 0;
58448 
58449  if (pData == NULL) {
58450  return MA_INVALID_ARGS;
58451  }
58452 
58453  /* Calculate the length from the linked list. */
58454  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)) {
58455  *pLength += pPage->sizeInFrames;
58456  }
58457 
58458  return MA_SUCCESS;
58459 }
58460 
58461 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)
58462 {
58464  ma_uint64 allocationSize;
58465 
58466  if (ppPage == NULL) {
58467  return MA_INVALID_ARGS;
58468  }
58469 
58470  *ppPage = NULL;
58471 
58472  if (pData == NULL) {
58473  return MA_INVALID_ARGS;
58474  }
58475 
58476  allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels));
58477  if (allocationSize > MA_SIZE_MAX) {
58478  return MA_OUT_OF_MEMORY; /* Too big. */
58479  }
58480 
58481  pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */
58482  if (pPage == NULL) {
58483  return MA_OUT_OF_MEMORY;
58484  }
58485 
58486  pPage->pNext = NULL;
58487  pPage->sizeInFrames = pageSizeInFrames;
58488 
58489  if (pInitialData != NULL) {
58490  ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels);
58491  }
58492 
58493  *ppPage = pPage;
58494 
58495  return MA_SUCCESS;
58496 }
58497 
58498 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)
58499 {
58500  if (pData == NULL || pPage == NULL) {
58501  return MA_INVALID_ARGS;
58502  }
58503 
58504  /* It's assumed the page is not attached to the list. */
58505  ma_free(pPage, pAllocationCallbacks);
58506 
58507  return MA_SUCCESS;
58508 }
58509 
58510 MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage)
58511 {
58512  if (pData == NULL || pPage == NULL) {
58513  return MA_INVALID_ARGS;
58514  }
58515 
58516  /* 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. */
58517 
58518  /* First thing to do is update the tail. */
58519  for (;;) {
58520  ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail);
58521  ma_paged_audio_buffer_page* pNewTail = pPage;
58522 
58523  if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) {
58524  /* 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. */
58525  ma_atomic_exchange_ptr(&pOldTail->pNext, pPage);
58526  break; /* Done. */
58527  }
58528  }
58529 
58530  return MA_SUCCESS;
58531 }
58532 
58533 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)
58534 {
58535  ma_result result;
58537 
58538  result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage);
58539  if (result != MA_SUCCESS) {
58540  return result;
58541  }
58542 
58543  return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */
58544 }
58545 
58546 
58547 MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData)
58548 {
58550 
58551  MA_ZERO_OBJECT(&config);
58552  config.pData = pData;
58553 
58554  return config;
58555 }
58556 
58557 
58558 static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
58559 {
58560  return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
58561 }
58562 
58563 static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
58564 {
58565  return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex);
58566 }
58567 
58568 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)
58569 {
58570  ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource;
58571 
58572  *pFormat = pPagedAudioBuffer->pData->format;
58573  *pChannels = pPagedAudioBuffer->pData->channels;
58574  *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
58575  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels);
58576 
58577  return MA_SUCCESS;
58578 }
58579 
58580 static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
58581 {
58582  return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor);
58583 }
58584 
58585 static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
58586 {
58587  return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength);
58588 }
58589 
58590 static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable =
58591 {
58592  ma_paged_audio_buffer__data_source_on_read,
58593  ma_paged_audio_buffer__data_source_on_seek,
58594  ma_paged_audio_buffer__data_source_on_get_data_format,
58595  ma_paged_audio_buffer__data_source_on_get_cursor,
58596  ma_paged_audio_buffer__data_source_on_get_length,
58597  NULL, /* onSetLooping */
58598  0
58599 };
58600 
58601 MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer)
58602 {
58603  ma_result result;
58604  ma_data_source_config dataSourceConfig;
58605 
58606  if (pPagedAudioBuffer == NULL) {
58607  return MA_INVALID_ARGS;
58608  }
58609 
58610  MA_ZERO_OBJECT(pPagedAudioBuffer);
58611 
58612  /* A config is required for the format and channel count. */
58613  if (pConfig == NULL) {
58614  return MA_INVALID_ARGS;
58615  }
58616 
58617  if (pConfig->pData == NULL) {
58618  return MA_INVALID_ARGS; /* No underlying data specified. */
58619  }
58620 
58621  dataSourceConfig = ma_data_source_config_init();
58622  dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable;
58623 
58624  result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds);
58625  if (result != MA_SUCCESS) {
58626  return result;
58627  }
58628 
58629  pPagedAudioBuffer->pData = pConfig->pData;
58630  pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData);
58631  pPagedAudioBuffer->relativeCursor = 0;
58632  pPagedAudioBuffer->absoluteCursor = 0;
58633 
58634  return MA_SUCCESS;
58635 }
58636 
58637 MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer)
58638 {
58639  if (pPagedAudioBuffer == NULL) {
58640  return;
58641  }
58642 
58643  /* Nothing to do. The data needs to be deleted separately. */
58644 }
58645 
58646 MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
58647 {
58648  ma_result result = MA_SUCCESS;
58649  ma_uint64 totalFramesRead = 0;
58650  ma_format format;
58651  ma_uint32 channels;
58652 
58653  if (pPagedAudioBuffer == NULL) {
58654  return MA_INVALID_ARGS;
58655  }
58656 
58657  format = pPagedAudioBuffer->pData->format;
58658  channels = pPagedAudioBuffer->pData->channels;
58659 
58660  while (totalFramesRead < frameCount) {
58661  /* Read from the current page. The buffer should never be in a state where this is NULL. */
58662  ma_uint64 framesRemainingInCurrentPage;
58663  ma_uint64 framesRemainingToRead = frameCount - totalFramesRead;
58664  ma_uint64 framesToReadThisIteration;
58665 
58666  MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL);
58667 
58668  framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor;
58669 
58670  framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead);
58671  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);
58672  totalFramesRead += framesToReadThisIteration;
58673 
58674  pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration;
58675  pPagedAudioBuffer->relativeCursor += framesToReadThisIteration;
58676 
58677  /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */
58678  MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames);
58679 
58680  if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) {
58681  /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */
58682  ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext);
58683  if (pNext == NULL) {
58684  result = MA_AT_END;
58685  break; /* We've reached the end. */
58686  } else {
58687  pPagedAudioBuffer->pCurrent = pNext;
58688  pPagedAudioBuffer->relativeCursor = 0;
58689  }
58690  }
58691  }
58692 
58693  if (pFramesRead != NULL) {
58694  *pFramesRead = totalFramesRead;
58695  }
58696 
58697  return result;
58698 }
58699 
58700 MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex)
58701 {
58702  if (pPagedAudioBuffer == NULL) {
58703  return MA_INVALID_ARGS;
58704  }
58705 
58706  if (frameIndex == pPagedAudioBuffer->absoluteCursor) {
58707  return MA_SUCCESS; /* Nothing to do. */
58708  }
58709 
58710  if (frameIndex < pPagedAudioBuffer->absoluteCursor) {
58711  /* Moving backwards. Need to move the cursor back to the start, and then move forward. */
58712  pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData);
58713  pPagedAudioBuffer->absoluteCursor = 0;
58714  pPagedAudioBuffer->relativeCursor = 0;
58715 
58716  /* Fall through to the forward seeking section below. */
58717  }
58718 
58719  if (frameIndex > pPagedAudioBuffer->absoluteCursor) {
58720  /* Moving forward. */
58722  ma_uint64 runningCursor = 0;
58723 
58724  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)) {
58725  ma_uint64 pageRangeBeg = runningCursor;
58726  ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames;
58727 
58728  if (frameIndex >= pageRangeBeg) {
58729  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. */
58730  /* We found the page. */
58731  pPagedAudioBuffer->pCurrent = pPage;
58732  pPagedAudioBuffer->absoluteCursor = frameIndex;
58733  pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg;
58734  return MA_SUCCESS;
58735  }
58736  }
58737 
58738  runningCursor = pageRangeEnd;
58739  }
58740 
58741  /* Getting here means we tried seeking too far forward. Don't change any state. */
58742  return MA_BAD_SEEK;
58743  }
58744 
58745  return MA_SUCCESS;
58746 }
58747 
58748 MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor)
58749 {
58750  if (pCursor == NULL) {
58751  return MA_INVALID_ARGS;
58752  }
58753 
58754  *pCursor = 0; /* Safety. */
58755 
58756  if (pPagedAudioBuffer == NULL) {
58757  return MA_INVALID_ARGS;
58758  }
58759 
58760  *pCursor = pPagedAudioBuffer->absoluteCursor;
58761 
58762  return MA_SUCCESS;
58763 }
58764 
58765 MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength)
58766 {
58767  return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength);
58768 }
58769 
58770 
58771 
58772 /**************************************************************************************************************************************************************
58773 
58774 VFS
58775 
58776 **************************************************************************************************************************************************************/
58777 MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
58778 {
58779  ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58780 
58781  if (pFile == NULL) {
58782  return MA_INVALID_ARGS;
58783  }
58784 
58785  *pFile = NULL;
58786 
58787  if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
58788  return MA_INVALID_ARGS;
58789  }
58790 
58791  if (pCallbacks->onOpen == NULL) {
58792  return MA_NOT_IMPLEMENTED;
58793  }
58794 
58795  return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile);
58796 }
58797 
58798 MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
58799 {
58800  ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58801 
58802  if (pFile == NULL) {
58803  return MA_INVALID_ARGS;
58804  }
58805 
58806  *pFile = NULL;
58807 
58808  if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
58809  return MA_INVALID_ARGS;
58810  }
58811 
58812  if (pCallbacks->onOpenW == NULL) {
58813  return MA_NOT_IMPLEMENTED;
58814  }
58815 
58816  return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile);
58817 }
58818 
58819 MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
58820 {
58821  ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58822 
58823  if (pVFS == NULL || file == NULL) {
58824  return MA_INVALID_ARGS;
58825  }
58826 
58827  if (pCallbacks->onClose == NULL) {
58828  return MA_NOT_IMPLEMENTED;
58829  }
58830 
58831  return pCallbacks->onClose(pVFS, file);
58832 }
58833 
58834 MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
58835 {
58836  ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58837  ma_result result;
58838  size_t bytesRead = 0;
58839 
58840  if (pBytesRead != NULL) {
58841  *pBytesRead = 0;
58842  }
58843 
58844  if (pVFS == NULL || file == NULL || pDst == NULL) {
58845  return MA_INVALID_ARGS;
58846  }
58847 
58848  if (pCallbacks->onRead == NULL) {
58849  return MA_NOT_IMPLEMENTED;
58850  }
58851 
58852  result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead);
58853 
58854  if (pBytesRead != NULL) {
58855  *pBytesRead = bytesRead;
58856  }
58857 
58858  if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) {
58859  result = MA_AT_END;
58860  }
58861 
58862  return result;
58863 }
58864 
58865 MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
58866 {
58867  ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58868 
58869  if (pBytesWritten != NULL) {
58870  *pBytesWritten = 0;
58871  }
58872 
58873  if (pVFS == NULL || file == NULL || pSrc == NULL) {
58874  return MA_INVALID_ARGS;
58875  }
58876 
58877  if (pCallbacks->onWrite == NULL) {
58878  return MA_NOT_IMPLEMENTED;
58879  }
58880 
58881  return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
58882 }
58883 
58884 MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
58885 {
58886  ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58887 
58888  if (pVFS == NULL || file == NULL) {
58889  return MA_INVALID_ARGS;
58890  }
58891 
58892  if (pCallbacks->onSeek == NULL) {
58893  return MA_NOT_IMPLEMENTED;
58894  }
58895 
58896  return pCallbacks->onSeek(pVFS, file, offset, origin);
58897 }
58898 
58899 MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
58900 {
58901  ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58902 
58903  if (pCursor == NULL) {
58904  return MA_INVALID_ARGS;
58905  }
58906 
58907  *pCursor = 0;
58908 
58909  if (pVFS == NULL || file == NULL) {
58910  return MA_INVALID_ARGS;
58911  }
58912 
58913  if (pCallbacks->onTell == NULL) {
58914  return MA_NOT_IMPLEMENTED;
58915  }
58916 
58917  return pCallbacks->onTell(pVFS, file, pCursor);
58918 }
58919 
58920 MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
58921 {
58922  ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58923 
58924  if (pInfo == NULL) {
58925  return MA_INVALID_ARGS;
58926  }
58927 
58928  MA_ZERO_OBJECT(pInfo);
58929 
58930  if (pVFS == NULL || file == NULL) {
58931  return MA_INVALID_ARGS;
58932  }
58933 
58934  if (pCallbacks->onInfo == NULL) {
58935  return MA_NOT_IMPLEMENTED;
58936  }
58937 
58938  return pCallbacks->onInfo(pVFS, file, pInfo);
58939 }
58940 
58941 
58942 #if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX))
58943  #define MA_USE_WIN32_FILEIO
58944 #endif
58945 
58946 #if defined(MA_USE_WIN32_FILEIO)
58947 /*
58948 We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do
58949 not have the Ex version. We therefore need to do some dynamic branching depending on what's available.
58950 
58951 We load these when we load our first file from the default VFS. It's left open for the life of the
58952 program and is left to the OS to uninitialize when the program terminates.
58953 */
58954 typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod);
58955 typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod);
58956 
58957 static ma_handle hKernel32DLL = NULL;
58958 static ma_SetFilePointer_proc ma_SetFilePointer = NULL;
58959 static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL;
58960 
58961 static void ma_win32_fileio_init(void)
58962 {
58963  if (hKernel32DLL == NULL) {
58964  hKernel32DLL = ma_dlopen(NULL, "kernel32.dll");
58965  if (hKernel32DLL != NULL) {
58966  ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer");
58967  ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx");
58968  }
58969  }
58970 }
58971 
58972 static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)
58973 {
58974  *pDesiredAccess = 0;
58975  if ((openMode & MA_OPEN_MODE_READ) != 0) {
58976  *pDesiredAccess |= GENERIC_READ;
58977  }
58978  if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
58979  *pDesiredAccess |= GENERIC_WRITE;
58980  }
58981 
58982  *pShareMode = 0;
58983  if ((openMode & MA_OPEN_MODE_READ) != 0) {
58984  *pShareMode |= FILE_SHARE_READ;
58985  }
58986 
58987  if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
58988  *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */
58989  } else {
58990  *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */
58991  }
58992 }
58993 
58994 static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
58995 {
58996  HANDLE hFile;
58997  DWORD dwDesiredAccess;
58998  DWORD dwShareMode;
58999  DWORD dwCreationDisposition;
59000 
59001  (void)pVFS;
59002 
59003  /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */
59004  ma_win32_fileio_init();
59005 
59006  ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
59007 
59008  hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
59009  if (hFile == INVALID_HANDLE_VALUE) {
59010  return ma_result_from_GetLastError(GetLastError());
59011  }
59012 
59013  *pFile = hFile;
59014  return MA_SUCCESS;
59015 }
59016 
59017 static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59018 {
59019  HANDLE hFile;
59020  DWORD dwDesiredAccess;
59021  DWORD dwShareMode;
59022  DWORD dwCreationDisposition;
59023 
59024  (void)pVFS;
59025 
59026  /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */
59027  ma_win32_fileio_init();
59028 
59029  ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
59030 
59031  hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
59032  if (hFile == INVALID_HANDLE_VALUE) {
59033  return ma_result_from_GetLastError(GetLastError());
59034  }
59035 
59036  *pFile = hFile;
59037  return MA_SUCCESS;
59038 }
59039 
59040 static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file)
59041 {
59042  (void)pVFS;
59043 
59044  if (CloseHandle((HANDLE)file) == 0) {
59045  return ma_result_from_GetLastError(GetLastError());
59046  }
59047 
59048  return MA_SUCCESS;
59049 }
59050 
59051 
59052 static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
59053 {
59054  ma_result result = MA_SUCCESS;
59055  size_t totalBytesRead;
59056 
59057  (void)pVFS;
59058 
59059  totalBytesRead = 0;
59060  while (totalBytesRead < sizeInBytes) {
59061  size_t bytesRemaining;
59062  DWORD bytesToRead;
59063  DWORD bytesRead;
59064  BOOL readResult;
59065 
59066  bytesRemaining = sizeInBytes - totalBytesRead;
59067  if (bytesRemaining >= 0xFFFFFFFF) {
59068  bytesToRead = 0xFFFFFFFF;
59069  } else {
59070  bytesToRead = (DWORD)bytesRemaining;
59071  }
59072 
59073  readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL);
59074  if (readResult == 1 && bytesRead == 0) {
59075  result = MA_AT_END;
59076  break; /* EOF */
59077  }
59078 
59079  totalBytesRead += bytesRead;
59080 
59081  if (bytesRead < bytesToRead) {
59082  break; /* EOF */
59083  }
59084 
59085  if (readResult == 0) {
59086  result = ma_result_from_GetLastError(GetLastError());
59087  break;
59088  }
59089  }
59090 
59091  if (pBytesRead != NULL) {
59092  *pBytesRead = totalBytesRead;
59093  }
59094 
59095  return result;
59096 }
59097 
59098 static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
59099 {
59100  ma_result result = MA_SUCCESS;
59101  size_t totalBytesWritten;
59102 
59103  (void)pVFS;
59104 
59105  totalBytesWritten = 0;
59106  while (totalBytesWritten < sizeInBytes) {
59107  size_t bytesRemaining;
59108  DWORD bytesToWrite;
59109  DWORD bytesWritten;
59110  BOOL writeResult;
59111 
59112  bytesRemaining = sizeInBytes - totalBytesWritten;
59113  if (bytesRemaining >= 0xFFFFFFFF) {
59114  bytesToWrite = 0xFFFFFFFF;
59115  } else {
59116  bytesToWrite = (DWORD)bytesRemaining;
59117  }
59118 
59119  writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL);
59120  totalBytesWritten += bytesWritten;
59121 
59122  if (writeResult == 0) {
59123  result = ma_result_from_GetLastError(GetLastError());
59124  break;
59125  }
59126  }
59127 
59128  if (pBytesWritten != NULL) {
59129  *pBytesWritten = totalBytesWritten;
59130  }
59131 
59132  return result;
59133 }
59134 
59135 
59136 static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
59137 {
59138  LARGE_INTEGER liDistanceToMove;
59139  DWORD dwMoveMethod;
59140  BOOL result;
59141 
59142  (void)pVFS;
59143 
59144  liDistanceToMove.QuadPart = offset;
59145 
59146  /* */ if (origin == ma_seek_origin_current) {
59147  dwMoveMethod = FILE_CURRENT;
59148  } else if (origin == ma_seek_origin_end) {
59149  dwMoveMethod = FILE_END;
59150  } else {
59151  dwMoveMethod = FILE_BEGIN;
59152  }
59153 
59154  if (ma_SetFilePointerEx != NULL) {
59155  result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
59156  } else if (ma_SetFilePointer != NULL) {
59157  /* No SetFilePointerEx() so restrict to 31 bits. */
59158  if (origin > 0x7FFFFFFF) {
59159  return MA_OUT_OF_RANGE;
59160  }
59161 
59162  result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);
59163  } else {
59164  return MA_NOT_IMPLEMENTED;
59165  }
59166 
59167  if (result == 0) {
59168  return ma_result_from_GetLastError(GetLastError());
59169  }
59170 
59171  return MA_SUCCESS;
59172 }
59173 
59174 static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
59175 {
59176  LARGE_INTEGER liZero;
59177  LARGE_INTEGER liTell;
59178  BOOL result;
59179 
59180  (void)pVFS;
59181 
59182  liZero.QuadPart = 0;
59183 
59184  if (ma_SetFilePointerEx != NULL) {
59185  result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);
59186  } else if (ma_SetFilePointer != NULL) {
59187  LONG tell;
59188 
59189  result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);
59190  liTell.QuadPart = tell;
59191  } else {
59192  return MA_NOT_IMPLEMENTED;
59193  }
59194 
59195  if (result == 0) {
59196  return ma_result_from_GetLastError(GetLastError());
59197  }
59198 
59199  if (pCursor != NULL) {
59200  *pCursor = liTell.QuadPart;
59201  }
59202 
59203  return MA_SUCCESS;
59204 }
59205 
59206 static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
59207 {
59208  BY_HANDLE_FILE_INFORMATION fi;
59209  BOOL result;
59210 
59211  (void)pVFS;
59212 
59213  result = GetFileInformationByHandle((HANDLE)file, &fi);
59214  if (result == 0) {
59215  return ma_result_from_GetLastError(GetLastError());
59216  }
59217 
59218  pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow);
59219 
59220  return MA_SUCCESS;
59221 }
59222 #else
59223 static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59224 {
59225  ma_result result;
59226  FILE* pFileStd;
59227  const char* pOpenModeStr;
59228 
59229  MA_ASSERT(pFilePath != NULL);
59230  MA_ASSERT(openMode != 0);
59231  MA_ASSERT(pFile != NULL);
59232 
59233  (void)pVFS;
59234 
59235  if ((openMode & MA_OPEN_MODE_READ) != 0) {
59236  if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
59237  pOpenModeStr = "r+";
59238  } else {
59239  pOpenModeStr = "rb";
59240  }
59241  } else {
59242  pOpenModeStr = "wb";
59243  }
59244 
59245  result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr);
59246  if (result != MA_SUCCESS) {
59247  return result;
59248  }
59249 
59250  *pFile = pFileStd;
59251 
59252  return MA_SUCCESS;
59253 }
59254 
59255 static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59256 {
59257  ma_result result;
59258  FILE* pFileStd;
59259  const wchar_t* pOpenModeStr;
59260 
59261  MA_ASSERT(pFilePath != NULL);
59262  MA_ASSERT(openMode != 0);
59263  MA_ASSERT(pFile != NULL);
59264 
59265  (void)pVFS;
59266 
59267  if ((openMode & MA_OPEN_MODE_READ) != 0) {
59268  if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
59269  pOpenModeStr = L"r+";
59270  } else {
59271  pOpenModeStr = L"rb";
59272  }
59273  } else {
59274  pOpenModeStr = L"wb";
59275  }
59276 
59277  result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL);
59278  if (result != MA_SUCCESS) {
59279  return result;
59280  }
59281 
59282  *pFile = pFileStd;
59283 
59284  return MA_SUCCESS;
59285 }
59286 
59287 static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file)
59288 {
59289  MA_ASSERT(file != NULL);
59290 
59291  (void)pVFS;
59292 
59293  fclose((FILE*)file);
59294 
59295  return MA_SUCCESS;
59296 }
59297 
59298 static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
59299 {
59300  size_t result;
59301 
59302  MA_ASSERT(file != NULL);
59303  MA_ASSERT(pDst != NULL);
59304 
59305  (void)pVFS;
59306 
59307  result = fread(pDst, 1, sizeInBytes, (FILE*)file);
59308 
59309  if (pBytesRead != NULL) {
59310  *pBytesRead = result;
59311  }
59312 
59313  if (result != sizeInBytes) {
59314  if (result == 0 && feof((FILE*)file)) {
59315  return MA_AT_END;
59316  } else {
59317  return ma_result_from_errno(ferror((FILE*)file));
59318  }
59319  }
59320 
59321  return MA_SUCCESS;
59322 }
59323 
59324 static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
59325 {
59326  size_t result;
59327 
59328  MA_ASSERT(file != NULL);
59329  MA_ASSERT(pSrc != NULL);
59330 
59331  (void)pVFS;
59332 
59333  result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file);
59334 
59335  if (pBytesWritten != NULL) {
59336  *pBytesWritten = result;
59337  }
59338 
59339  if (result != sizeInBytes) {
59340  return ma_result_from_errno(ferror((FILE*)file));
59341  }
59342 
59343  return MA_SUCCESS;
59344 }
59345 
59346 static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
59347 {
59348  int result;
59349  int whence;
59350 
59351  MA_ASSERT(file != NULL);
59352 
59353  (void)pVFS;
59354 
59355  if (origin == ma_seek_origin_start) {
59356  whence = SEEK_SET;
59357  } else if (origin == ma_seek_origin_end) {
59358  whence = SEEK_END;
59359  } else {
59360  whence = SEEK_CUR;
59361  }
59362 
59363 #if defined(_WIN32)
59364  #if defined(_MSC_VER) && _MSC_VER > 1200
59365  result = _fseeki64((FILE*)file, offset, whence);
59366  #else
59367  /* No _fseeki64() so restrict to 31 bits. */
59368  if (origin > 0x7FFFFFFF) {
59369  return MA_OUT_OF_RANGE;
59370  }
59371 
59372  result = fseek((FILE*)file, (int)offset, whence);
59373  #endif
59374 #else
59375  result = fseek((FILE*)file, (long int)offset, whence);
59376 #endif
59377  if (result != 0) {
59378  return MA_ERROR;
59379  }
59380 
59381  return MA_SUCCESS;
59382 }
59383 
59384 static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
59385 {
59386  ma_int64 result;
59387 
59388  MA_ASSERT(file != NULL);
59389  MA_ASSERT(pCursor != NULL);
59390 
59391  (void)pVFS;
59392 
59393 #if defined(_WIN32)
59394  #if defined(_MSC_VER) && _MSC_VER > 1200
59395  result = _ftelli64((FILE*)file);
59396  #else
59397  result = ftell((FILE*)file);
59398  #endif
59399 #else
59400  result = ftell((FILE*)file);
59401 #endif
59402 
59403  *pCursor = result;
59404 
59405  return MA_SUCCESS;
59406 }
59407 
59408 #if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD)
59409 int fileno(FILE *stream);
59410 #endif
59411 
59412 static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
59413 {
59414  int fd;
59415  struct stat info;
59416 
59417  MA_ASSERT(file != NULL);
59418  MA_ASSERT(pInfo != NULL);
59419 
59420  (void)pVFS;
59421 
59422 #if defined(_MSC_VER)
59423  fd = _fileno((FILE*)file);
59424 #else
59425  fd = fileno((FILE*)file);
59426 #endif
59427 
59428  if (fstat(fd, &info) != 0) {
59429  return ma_result_from_errno(errno);
59430  }
59431 
59432  pInfo->sizeInBytes = info.st_size;
59433 
59434  return MA_SUCCESS;
59435 }
59436 #endif
59437 
59438 
59439 static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59440 {
59441  if (pFile == NULL) {
59442  return MA_INVALID_ARGS;
59443  }
59444 
59445  *pFile = NULL;
59446 
59447  if (pFilePath == NULL || openMode == 0) {
59448  return MA_INVALID_ARGS;
59449  }
59450 
59451 #if defined(MA_USE_WIN32_FILEIO)
59452  return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);
59453 #else
59454  return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);
59455 #endif
59456 }
59457 
59458 static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59459 {
59460  if (pFile == NULL) {
59461  return MA_INVALID_ARGS;
59462  }
59463 
59464  *pFile = NULL;
59465 
59466  if (pFilePath == NULL || openMode == 0) {
59467  return MA_INVALID_ARGS;
59468  }
59469 
59470 #if defined(MA_USE_WIN32_FILEIO)
59471  return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);
59472 #else
59473  return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);
59474 #endif
59475 }
59476 
59477 static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
59478 {
59479  if (file == NULL) {
59480  return MA_INVALID_ARGS;
59481  }
59482 
59483 #if defined(MA_USE_WIN32_FILEIO)
59484  return ma_default_vfs_close__win32(pVFS, file);
59485 #else
59486  return ma_default_vfs_close__stdio(pVFS, file);
59487 #endif
59488 }
59489 
59490 static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
59491 {
59492  if (pBytesRead != NULL) {
59493  *pBytesRead = 0;
59494  }
59495 
59496  if (file == NULL || pDst == NULL) {
59497  return MA_INVALID_ARGS;
59498  }
59499 
59500 #if defined(MA_USE_WIN32_FILEIO)
59501  return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);
59502 #else
59503  return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);
59504 #endif
59505 }
59506 
59507 static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
59508 {
59509  if (pBytesWritten != NULL) {
59510  *pBytesWritten = 0;
59511  }
59512 
59513  if (file == NULL || pSrc == NULL) {
59514  return MA_INVALID_ARGS;
59515  }
59516 
59517 #if defined(MA_USE_WIN32_FILEIO)
59518  return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
59519 #else
59520  return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
59521 #endif
59522 }
59523 
59524 static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
59525 {
59526  if (file == NULL) {
59527  return MA_INVALID_ARGS;
59528  }
59529 
59530 #if defined(MA_USE_WIN32_FILEIO)
59531  return ma_default_vfs_seek__win32(pVFS, file, offset, origin);
59532 #else
59533  return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);
59534 #endif
59535 }
59536 
59537 static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
59538 {
59539  if (pCursor == NULL) {
59540  return MA_INVALID_ARGS;
59541  }
59542 
59543  *pCursor = 0;
59544 
59545  if (file == NULL) {
59546  return MA_INVALID_ARGS;
59547  }
59548 
59549 #if defined(MA_USE_WIN32_FILEIO)
59550  return ma_default_vfs_tell__win32(pVFS, file, pCursor);
59551 #else
59552  return ma_default_vfs_tell__stdio(pVFS, file, pCursor);
59553 #endif
59554 }
59555 
59556 static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
59557 {
59558  if (pInfo == NULL) {
59559  return MA_INVALID_ARGS;
59560  }
59561 
59562  MA_ZERO_OBJECT(pInfo);
59563 
59564  if (file == NULL) {
59565  return MA_INVALID_ARGS;
59566  }
59567 
59568 #if defined(MA_USE_WIN32_FILEIO)
59569  return ma_default_vfs_info__win32(pVFS, file, pInfo);
59570 #else
59571  return ma_default_vfs_info__stdio(pVFS, file, pInfo);
59572 #endif
59573 }
59574 
59575 
59576 MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks)
59577 {
59578  if (pVFS == NULL) {
59579  return MA_INVALID_ARGS;
59580  }
59581 
59582  pVFS->cb.onOpen = ma_default_vfs_open;
59583  pVFS->cb.onOpenW = ma_default_vfs_open_w;
59584  pVFS->cb.onClose = ma_default_vfs_close;
59585  pVFS->cb.onRead = ma_default_vfs_read;
59586  pVFS->cb.onWrite = ma_default_vfs_write;
59587  pVFS->cb.onSeek = ma_default_vfs_seek;
59588  pVFS->cb.onTell = ma_default_vfs_tell;
59589  pVFS->cb.onInfo = ma_default_vfs_info;
59590  ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks);
59591 
59592  return MA_SUCCESS;
59593 }
59594 
59595 
59596 MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59597 {
59598  if (pVFS != NULL) {
59599  return ma_vfs_open(pVFS, pFilePath, openMode, pFile);
59600  } else {
59601  return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile);
59602  }
59603 }
59604 
59605 MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59606 {
59607  if (pVFS != NULL) {
59608  return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile);
59609  } else {
59610  return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile);
59611  }
59612 }
59613 
59614 MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file)
59615 {
59616  if (pVFS != NULL) {
59617  return ma_vfs_close(pVFS, file);
59618  } else {
59619  return ma_default_vfs_close(pVFS, file);
59620  }
59621 }
59622 
59623 MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
59624 {
59625  if (pVFS != NULL) {
59626  return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
59627  } else {
59628  return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
59629  }
59630 }
59631 
59632 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)
59633 {
59634  if (pVFS != NULL) {
59635  return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
59636  } else {
59637  return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
59638  }
59639 }
59640 
59641 MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
59642 {
59643  if (pVFS != NULL) {
59644  return ma_vfs_seek(pVFS, file, offset, origin);
59645  } else {
59646  return ma_default_vfs_seek(pVFS, file, offset, origin);
59647  }
59648 }
59649 
59650 MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
59651 {
59652  if (pVFS != NULL) {
59653  return ma_vfs_tell(pVFS, file, pCursor);
59654  } else {
59655  return ma_default_vfs_tell(pVFS, file, pCursor);
59656  }
59657 }
59658 
59659 MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
59660 {
59661  if (pVFS != NULL) {
59662  return ma_vfs_info(pVFS, file, pInfo);
59663  } else {
59664  return ma_default_vfs_info(pVFS, file, pInfo);
59665  }
59666 }
59667 
59668 
59669 
59670 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)
59671 {
59672  ma_result result;
59673  ma_vfs_file file;
59674  ma_file_info info;
59675  void* pData;
59676  size_t bytesRead;
59677 
59678  if (ppData != NULL) {
59679  *ppData = NULL;
59680  }
59681  if (pSize != NULL) {
59682  *pSize = 0;
59683  }
59684 
59685  if (ppData == NULL) {
59686  return MA_INVALID_ARGS;
59687  }
59688 
59689  if (pFilePath != NULL) {
59690  result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
59691  } else {
59692  result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file);
59693  }
59694  if (result != MA_SUCCESS) {
59695  return result;
59696  }
59697 
59698  result = ma_vfs_or_default_info(pVFS, file, &info);
59699  if (result != MA_SUCCESS) {
59700  ma_vfs_or_default_close(pVFS, file);
59701  return result;
59702  }
59703 
59704  if (info.sizeInBytes > MA_SIZE_MAX) {
59705  ma_vfs_or_default_close(pVFS, file);
59706  return MA_TOO_BIG;
59707  }
59708 
59709  pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */
59710  if (pData == NULL) {
59711  ma_vfs_or_default_close(pVFS, file);
59712  return result;
59713  }
59714 
59715  result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */
59716  ma_vfs_or_default_close(pVFS, file);
59717 
59718  if (result != MA_SUCCESS) {
59719  ma_free(pData, pAllocationCallbacks);
59720  return result;
59721  }
59722 
59723  if (pSize != NULL) {
59724  *pSize = bytesRead;
59725  }
59726 
59727  MA_ASSERT(ppData != NULL);
59728  *ppData = pData;
59729 
59730  return MA_SUCCESS;
59731 }
59732 
59733 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)
59734 {
59735  return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks);
59736 }
59737 
59738 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)
59739 {
59740  return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks);
59741 }
59742 
59743 
59744 
59745 /**************************************************************************************************************************************************************
59746 
59747 Decoding and Encoding Headers. These are auto-generated from a tool.
59748 
59749 **************************************************************************************************************************************************************/
59750 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
59751 /* dr_wav_h begin */
59752 #ifndef ma_dr_wav_h
59753 #define ma_dr_wav_h
59754 #ifdef __cplusplus
59755 extern "C" {
59756 #endif
59757 #define MA_DR_WAV_STRINGIFY(x) #x
59758 #define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x)
59759 #define MA_DR_WAV_VERSION_MAJOR 0
59760 #define MA_DR_WAV_VERSION_MINOR 13
59761 #define MA_DR_WAV_VERSION_REVISION 13
59762 #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)
59763 #include <stddef.h>
59764 #define MA_DR_WAVE_FORMAT_PCM 0x1
59765 #define MA_DR_WAVE_FORMAT_ADPCM 0x2
59766 #define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3
59767 #define MA_DR_WAVE_FORMAT_ALAW 0x6
59768 #define MA_DR_WAVE_FORMAT_MULAW 0x7
59769 #define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11
59770 #define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
59771 #define MA_DR_WAV_SEQUENTIAL 0x00000001
59772 #define MA_DR_WAV_WITH_METADATA 0x00000002
59773 MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
59774 MA_API const char* ma_dr_wav_version_string(void);
59775 typedef enum
59776 {
59777  ma_dr_wav_seek_origin_start,
59778  ma_dr_wav_seek_origin_current
59779 } ma_dr_wav_seek_origin;
59780 typedef enum
59781 {
59782  ma_dr_wav_container_riff,
59783  ma_dr_wav_container_rifx,
59784  ma_dr_wav_container_w64,
59785  ma_dr_wav_container_rf64,
59786  ma_dr_wav_container_aiff
59787 } ma_dr_wav_container;
59788 typedef struct
59789 {
59790  union
59791  {
59792  ma_uint8 fourcc[4];
59793  ma_uint8 guid[16];
59794  } id;
59795  ma_uint64 sizeInBytes;
59796  unsigned int paddingSize;
59797 } ma_dr_wav_chunk_header;
59798 typedef struct
59799 {
59800  ma_uint16 formatTag;
59801  ma_uint16 channels;
59802  ma_uint32 sampleRate;
59803  ma_uint32 avgBytesPerSec;
59804  ma_uint16 blockAlign;
59805  ma_uint16 bitsPerSample;
59806  ma_uint16 extendedSize;
59807  ma_uint16 validBitsPerSample;
59808  ma_uint32 channelMask;
59809  ma_uint8 subFormat[16];
59810 } ma_dr_wav_fmt;
59811 MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT);
59812 typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
59813 typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
59814 typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin);
59815 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);
59816 typedef struct
59817 {
59818  const ma_uint8* data;
59819  size_t dataSize;
59820  size_t currentReadPos;
59821 } ma_dr_wav__memory_stream;
59822 typedef struct
59823 {
59824  void** ppData;
59825  size_t* pDataSize;
59826  size_t dataSize;
59827  size_t dataCapacity;
59828  size_t currentWritePos;
59829 } ma_dr_wav__memory_stream_write;
59830 typedef struct
59831 {
59832  ma_dr_wav_container container;
59833  ma_uint32 format;
59834  ma_uint32 channels;
59835  ma_uint32 sampleRate;
59836  ma_uint32 bitsPerSample;
59837 } ma_dr_wav_data_format;
59838 typedef enum
59839 {
59840  ma_dr_wav_metadata_type_none = 0,
59841  ma_dr_wav_metadata_type_unknown = 1 << 0,
59842  ma_dr_wav_metadata_type_smpl = 1 << 1,
59843  ma_dr_wav_metadata_type_inst = 1 << 2,
59844  ma_dr_wav_metadata_type_cue = 1 << 3,
59845  ma_dr_wav_metadata_type_acid = 1 << 4,
59846  ma_dr_wav_metadata_type_bext = 1 << 5,
59847  ma_dr_wav_metadata_type_list_label = 1 << 6,
59848  ma_dr_wav_metadata_type_list_note = 1 << 7,
59849  ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8,
59850  ma_dr_wav_metadata_type_list_info_software = 1 << 9,
59851  ma_dr_wav_metadata_type_list_info_copyright = 1 << 10,
59852  ma_dr_wav_metadata_type_list_info_title = 1 << 11,
59853  ma_dr_wav_metadata_type_list_info_artist = 1 << 12,
59854  ma_dr_wav_metadata_type_list_info_comment = 1 << 13,
59855  ma_dr_wav_metadata_type_list_info_date = 1 << 14,
59856  ma_dr_wav_metadata_type_list_info_genre = 1 << 15,
59857  ma_dr_wav_metadata_type_list_info_album = 1 << 16,
59858  ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17,
59859  ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software
59860  | ma_dr_wav_metadata_type_list_info_copyright
59861  | ma_dr_wav_metadata_type_list_info_title
59862  | ma_dr_wav_metadata_type_list_info_artist
59863  | ma_dr_wav_metadata_type_list_info_comment
59864  | ma_dr_wav_metadata_type_list_info_date
59865  | ma_dr_wav_metadata_type_list_info_genre
59866  | ma_dr_wav_metadata_type_list_info_album
59867  | ma_dr_wav_metadata_type_list_info_tracknumber,
59868  ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label
59869  | ma_dr_wav_metadata_type_list_note
59870  | ma_dr_wav_metadata_type_list_labelled_cue_region,
59871  ma_dr_wav_metadata_type_all = -2,
59872  ma_dr_wav_metadata_type_all_including_unknown = -1
59873 } ma_dr_wav_metadata_type;
59874 typedef enum
59875 {
59876  ma_dr_wav_smpl_loop_type_forward = 0,
59877  ma_dr_wav_smpl_loop_type_pingpong = 1,
59878  ma_dr_wav_smpl_loop_type_backward = 2
59879 } ma_dr_wav_smpl_loop_type;
59880 typedef struct
59881 {
59882  ma_uint32 cuePointId;
59883  ma_uint32 type;
59884  ma_uint32 firstSampleByteOffset;
59885  ma_uint32 lastSampleByteOffset;
59886  ma_uint32 sampleFraction;
59887  ma_uint32 playCount;
59888 } ma_dr_wav_smpl_loop;
59889 typedef struct
59890 {
59891  ma_uint32 manufacturerId;
59892  ma_uint32 productId;
59893  ma_uint32 samplePeriodNanoseconds;
59894  ma_uint32 midiUnityNote;
59895  ma_uint32 midiPitchFraction;
59896  ma_uint32 smpteFormat;
59897  ma_uint32 smpteOffset;
59898  ma_uint32 sampleLoopCount;
59899  ma_uint32 samplerSpecificDataSizeInBytes;
59900  ma_dr_wav_smpl_loop* pLoops;
59901  ma_uint8* pSamplerSpecificData;
59902 } ma_dr_wav_smpl;
59903 typedef struct
59904 {
59905  ma_int8 midiUnityNote;
59906  ma_int8 fineTuneCents;
59907  ma_int8 gainDecibels;
59908  ma_int8 lowNote;
59909  ma_int8 highNote;
59910  ma_int8 lowVelocity;
59911  ma_int8 highVelocity;
59912 } ma_dr_wav_inst;
59913 typedef struct
59914 {
59915  ma_uint32 id;
59916  ma_uint32 playOrderPosition;
59917  ma_uint8 dataChunkId[4];
59918  ma_uint32 chunkStart;
59919  ma_uint32 blockStart;
59920  ma_uint32 sampleByteOffset;
59921 } ma_dr_wav_cue_point;
59922 typedef struct
59923 {
59924  ma_uint32 cuePointCount;
59925  ma_dr_wav_cue_point *pCuePoints;
59926 } ma_dr_wav_cue;
59927 typedef enum
59928 {
59929  ma_dr_wav_acid_flag_one_shot = 1,
59930  ma_dr_wav_acid_flag_root_note_set = 2,
59931  ma_dr_wav_acid_flag_stretch = 4,
59932  ma_dr_wav_acid_flag_disk_based = 8,
59933  ma_dr_wav_acid_flag_acidizer = 16
59934 } ma_dr_wav_acid_flag;
59935 typedef struct
59936 {
59937  ma_uint32 flags;
59938  ma_uint16 midiUnityNote;
59939  ma_uint16 reserved1;
59940  float reserved2;
59941  ma_uint32 numBeats;
59942  ma_uint16 meterDenominator;
59943  ma_uint16 meterNumerator;
59944  float tempo;
59945 } ma_dr_wav_acid;
59946 typedef struct
59947 {
59948  ma_uint32 cuePointId;
59949  ma_uint32 stringLength;
59950  char* pString;
59951 } ma_dr_wav_list_label_or_note;
59952 typedef struct
59953 {
59954  char* pDescription;
59955  char* pOriginatorName;
59956  char* pOriginatorReference;
59957  char pOriginationDate[10];
59958  char pOriginationTime[8];
59959  ma_uint64 timeReference;
59960  ma_uint16 version;
59961  char* pCodingHistory;
59962  ma_uint32 codingHistorySize;
59963  ma_uint8* pUMID;
59964  ma_uint16 loudnessValue;
59965  ma_uint16 loudnessRange;
59966  ma_uint16 maxTruePeakLevel;
59967  ma_uint16 maxMomentaryLoudness;
59968  ma_uint16 maxShortTermLoudness;
59969 } ma_dr_wav_bext;
59970 typedef struct
59971 {
59972  ma_uint32 stringLength;
59973  char* pString;
59974 } ma_dr_wav_list_info_text;
59975 typedef struct
59976 {
59977  ma_uint32 cuePointId;
59978  ma_uint32 sampleLength;
59979  ma_uint8 purposeId[4];
59980  ma_uint16 country;
59981  ma_uint16 language;
59982  ma_uint16 dialect;
59983  ma_uint16 codePage;
59984  ma_uint32 stringLength;
59985  char* pString;
59986 } ma_dr_wav_list_labelled_cue_region;
59987 typedef enum
59988 {
59989  ma_dr_wav_metadata_location_invalid,
59990  ma_dr_wav_metadata_location_top_level,
59991  ma_dr_wav_metadata_location_inside_info_list,
59992  ma_dr_wav_metadata_location_inside_adtl_list
59993 } ma_dr_wav_metadata_location;
59994 typedef struct
59995 {
59996  ma_uint8 id[4];
59997  ma_dr_wav_metadata_location chunkLocation;
59998  ma_uint32 dataSizeInBytes;
59999  ma_uint8* pData;
60000 } ma_dr_wav_unknown_metadata;
60001 typedef struct
60002 {
60003  ma_dr_wav_metadata_type type;
60004  union
60005  {
60006  ma_dr_wav_cue cue;
60007  ma_dr_wav_smpl smpl;
60008  ma_dr_wav_acid acid;
60009  ma_dr_wav_inst inst;
60010  ma_dr_wav_bext bext;
60011  ma_dr_wav_list_label_or_note labelOrNote;
60012  ma_dr_wav_list_labelled_cue_region labelledCueRegion;
60013  ma_dr_wav_list_info_text infoText;
60014  ma_dr_wav_unknown_metadata unknown;
60015  } data;
60016 } ma_dr_wav_metadata;
60017 typedef struct
60018 {
60019  ma_dr_wav_read_proc onRead;
60020  ma_dr_wav_write_proc onWrite;
60021  ma_dr_wav_seek_proc onSeek;
60022  void* pUserData;
60023  ma_allocation_callbacks allocationCallbacks;
60024  ma_dr_wav_container container;
60025  ma_dr_wav_fmt fmt;
60026  ma_uint32 sampleRate;
60027  ma_uint16 channels;
60028  ma_uint16 bitsPerSample;
60029  ma_uint16 translatedFormatTag;
60030  ma_uint64 totalPCMFrameCount;
60031  ma_uint64 dataChunkDataSize;
60032  ma_uint64 dataChunkDataPos;
60033  ma_uint64 bytesRemaining;
60034  ma_uint64 readCursorInPCMFrames;
60035  ma_uint64 dataChunkDataSizeTargetWrite;
60036  ma_bool32 isSequentialWrite;
60037  ma_dr_wav_metadata* pMetadata;
60038  ma_uint32 metadataCount;
60039  ma_dr_wav__memory_stream memoryStream;
60040  ma_dr_wav__memory_stream_write memoryStreamWrite;
60041  struct
60042  {
60043  ma_uint32 bytesRemainingInBlock;
60044  ma_uint16 predictor[2];
60045  ma_int32 delta[2];
60046  ma_int32 cachedFrames[4];
60047  ma_uint32 cachedFrameCount;
60048  ma_int32 prevFrames[2][2];
60049  } msadpcm;
60050  struct
60051  {
60052  ma_uint32 bytesRemainingInBlock;
60053  ma_int32 predictor[2];
60054  ma_int32 stepIndex[2];
60055  ma_int32 cachedFrames[16];
60056  ma_uint32 cachedFrameCount;
60057  } ima;
60058  struct
60059  {
60060  ma_bool8 isLE;
60061  ma_bool8 isUnsigned;
60062  } aiff;
60063 } ma_dr_wav;
60064 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);
60065 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);
60066 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);
60067 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);
60068 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);
60069 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);
60070 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);
60071 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);
60072 MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav);
60073 MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav);
60074 MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut);
60075 MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
60076 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
60077 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
60078 MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex);
60079 MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor);
60080 MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength);
60081 MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData);
60082 MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
60083 MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
60084 MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
60085 #ifndef MA_DR_WAV_NO_CONVERSION_API
60086 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
60087 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
60088 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
60089 MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
60090 MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
60091 MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount);
60092 MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount);
60093 MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount);
60094 MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
60095 MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
60096 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
60097 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
60098 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
60099 MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
60100 MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount);
60101 MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
60102 MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount);
60103 MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
60104 MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
60105 MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
60106 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
60107 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
60108 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
60109 MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
60110 MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount);
60111 MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
60112 MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount);
60113 MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount);
60114 MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
60115 MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
60116 #endif
60117 #ifndef MA_DR_WAV_NO_STDIO
60118 MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks);
60119 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);
60120 MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks);
60121 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);
60122 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);
60123 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);
60124 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);
60125 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);
60126 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);
60127 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);
60128 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);
60129 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);
60130 #endif
60131 MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
60132 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);
60133 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);
60134 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);
60135 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);
60136 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);
60137 #ifndef MA_DR_WAV_NO_CONVERSION_API
60138 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);
60139 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);
60140 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);
60141 #ifndef MA_DR_WAV_NO_STDIO
60142 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);
60143 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);
60144 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);
60145 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);
60146 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);
60147 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);
60148 #endif
60149 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);
60150 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);
60151 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);
60152 #endif
60153 MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
60154 MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data);
60155 MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data);
60156 MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data);
60157 MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data);
60158 MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data);
60159 MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data);
60160 MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data);
60161 MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]);
60162 MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b);
60163 #ifdef __cplusplus
60164 }
60165 #endif
60166 #endif
60167 /* dr_wav_h end */
60168 #endif /* MA_NO_WAV */
60169 
60170 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
60171 /* dr_flac_h begin */
60172 #ifndef ma_dr_flac_h
60173 #define ma_dr_flac_h
60174 #ifdef __cplusplus
60175 extern "C" {
60176 #endif
60177 #define MA_DR_FLAC_STRINGIFY(x) #x
60178 #define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x)
60179 #define MA_DR_FLAC_VERSION_MAJOR 0
60180 #define MA_DR_FLAC_VERSION_MINOR 12
60181 #define MA_DR_FLAC_VERSION_REVISION 42
60182 #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)
60183 #include <stddef.h>
60184 #if defined(_MSC_VER) && _MSC_VER >= 1700
60185  #define MA_DR_FLAC_DEPRECATED __declspec(deprecated)
60186 #elif (defined(__GNUC__) && __GNUC__ >= 4)
60187  #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated))
60188 #elif defined(__has_feature)
60189  #if __has_feature(attribute_deprecated)
60190  #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated))
60191  #else
60192  #define MA_DR_FLAC_DEPRECATED
60193  #endif
60194 #else
60195  #define MA_DR_FLAC_DEPRECATED
60196 #endif
60197 MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
60198 MA_API const char* ma_dr_flac_version_string(void);
60199 #ifndef MA_DR_FLAC_BUFFER_SIZE
60200 #define MA_DR_FLAC_BUFFER_SIZE 4096
60201 #endif
60202 #ifdef MA_64BIT
60203 typedef ma_uint64 ma_dr_flac_cache_t;
60204 #else
60205 typedef ma_uint32 ma_dr_flac_cache_t;
60206 #endif
60207 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
60208 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1
60209 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2
60210 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
60211 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
60212 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5
60213 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6
60214 #define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127
60215 #define MA_DR_FLAC_PICTURE_TYPE_OTHER 0
60216 #define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1
60217 #define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
60218 #define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3
60219 #define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4
60220 #define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5
60221 #define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6
60222 #define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7
60223 #define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8
60224 #define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9
60225 #define MA_DR_FLAC_PICTURE_TYPE_BAND 10
60226 #define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11
60227 #define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12
60228 #define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13
60229 #define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14
60230 #define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
60231 #define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
60232 #define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
60233 #define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18
60234 #define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
60235 #define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
60236 typedef enum
60237 {
60238  ma_dr_flac_container_native,
60239  ma_dr_flac_container_ogg,
60240  ma_dr_flac_container_unknown
60241 } ma_dr_flac_container;
60242 typedef enum
60243 {
60244  ma_dr_flac_seek_origin_start,
60245  ma_dr_flac_seek_origin_current
60246 } ma_dr_flac_seek_origin;
60247 typedef struct
60248 {
60249  ma_uint64 firstPCMFrame;
60250  ma_uint64 flacFrameOffset;
60251  ma_uint16 pcmFrameCount;
60252 } ma_dr_flac_seekpoint;
60253 typedef struct
60254 {
60255  ma_uint16 minBlockSizeInPCMFrames;
60256  ma_uint16 maxBlockSizeInPCMFrames;
60257  ma_uint32 minFrameSizeInPCMFrames;
60258  ma_uint32 maxFrameSizeInPCMFrames;
60259  ma_uint32 sampleRate;
60260  ma_uint8 channels;
60261  ma_uint8 bitsPerSample;
60262  ma_uint64 totalPCMFrameCount;
60263  ma_uint8 md5[16];
60264 } ma_dr_flac_streaminfo;
60265 typedef struct
60266 {
60267  ma_uint32 type;
60268  const void* pRawData;
60269  ma_uint32 rawDataSize;
60270  union
60271  {
60272  ma_dr_flac_streaminfo streaminfo;
60273  struct
60274  {
60275  int unused;
60276  } padding;
60277  struct
60278  {
60279  ma_uint32 id;
60280  const void* pData;
60281  ma_uint32 dataSize;
60282  } application;
60283  struct
60284  {
60285  ma_uint32 seekpointCount;
60286  const ma_dr_flac_seekpoint* pSeekpoints;
60287  } seektable;
60288  struct
60289  {
60290  ma_uint32 vendorLength;
60291  const char* vendor;
60292  ma_uint32 commentCount;
60293  const void* pComments;
60294  } vorbis_comment;
60295  struct
60296  {
60297  char catalog[128];
60298  ma_uint64 leadInSampleCount;
60299  ma_bool32 isCD;
60300  ma_uint8 trackCount;
60301  const void* pTrackData;
60302  } cuesheet;
60303  struct
60304  {
60305  ma_uint32 type;
60306  ma_uint32 mimeLength;
60307  const char* mime;
60308  ma_uint32 descriptionLength;
60309  const char* description;
60310  ma_uint32 width;
60311  ma_uint32 height;
60312  ma_uint32 colorDepth;
60313  ma_uint32 indexColorCount;
60314  ma_uint32 pictureDataSize;
60315  const ma_uint8* pPictureData;
60316  } picture;
60317  } data;
60318 } ma_dr_flac_metadata;
60319 typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
60320 typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin);
60321 typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata);
60322 typedef struct
60323 {
60324  const ma_uint8* data;
60325  size_t dataSize;
60326  size_t currentReadPos;
60327 } ma_dr_flac__memory_stream;
60328 typedef struct
60329 {
60330  ma_dr_flac_read_proc onRead;
60331  ma_dr_flac_seek_proc onSeek;
60332  void* pUserData;
60333  size_t unalignedByteCount;
60334  ma_dr_flac_cache_t unalignedCache;
60335  ma_uint32 nextL2Line;
60336  ma_uint32 consumedBits;
60337  ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)];
60338  ma_dr_flac_cache_t cache;
60339  ma_uint16 crc16;
60340  ma_dr_flac_cache_t crc16Cache;
60341  ma_uint32 crc16CacheIgnoredBytes;
60342 } ma_dr_flac_bs;
60343 typedef struct
60344 {
60345  ma_uint8 subframeType;
60346  ma_uint8 wastedBitsPerSample;
60347  ma_uint8 lpcOrder;
60348  ma_int32* pSamplesS32;
60349 } ma_dr_flac_subframe;
60350 typedef struct
60351 {
60352  ma_uint64 pcmFrameNumber;
60353  ma_uint32 flacFrameNumber;
60354  ma_uint32 sampleRate;
60355  ma_uint16 blockSizeInPCMFrames;
60356  ma_uint8 channelAssignment;
60357  ma_uint8 bitsPerSample;
60358  ma_uint8 crc8;
60359 } ma_dr_flac_frame_header;
60360 typedef struct
60361 {
60362  ma_dr_flac_frame_header header;
60363  ma_uint32 pcmFramesRemaining;
60364  ma_dr_flac_subframe subframes[8];
60365 } ma_dr_flac_frame;
60366 typedef struct
60367 {
60368  ma_dr_flac_meta_proc onMeta;
60369  void* pUserDataMD;
60370  ma_allocation_callbacks allocationCallbacks;
60371  ma_uint32 sampleRate;
60372  ma_uint8 channels;
60373  ma_uint8 bitsPerSample;
60374  ma_uint16 maxBlockSizeInPCMFrames;
60375  ma_uint64 totalPCMFrameCount;
60376  ma_dr_flac_container container;
60377  ma_uint32 seekpointCount;
60378  ma_dr_flac_frame currentFLACFrame;
60379  ma_uint64 currentPCMFrame;
60380  ma_uint64 firstFLACFramePosInBytes;
60381  ma_dr_flac__memory_stream memoryStream;
60382  ma_int32* pDecodedSamples;
60383  ma_dr_flac_seekpoint* pSeekpoints;
60384  void* _oggbs;
60385  ma_bool32 _noSeekTableSeek : 1;
60386  ma_bool32 _noBinarySearchSeek : 1;
60387  ma_bool32 _noBruteForceSeek : 1;
60388  ma_dr_flac_bs bs;
60389  ma_uint8 pExtraData[1];
60390 } ma_dr_flac;
60391 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);
60392 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);
60393 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);
60394 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);
60395 MA_API void ma_dr_flac_close(ma_dr_flac* pFlac);
60396 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut);
60397 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut);
60398 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut);
60399 MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex);
60400 #ifndef MA_DR_FLAC_NO_STDIO
60401 MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks);
60402 MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks);
60403 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);
60404 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);
60405 #endif
60406 MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
60407 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);
60408 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);
60409 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);
60410 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);
60411 #ifndef MA_DR_FLAC_NO_STDIO
60412 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);
60413 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);
60414 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);
60415 #endif
60416 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);
60417 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);
60418 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);
60419 MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
60420 typedef struct
60421 {
60422  ma_uint32 countRemaining;
60423  const char* pRunningData;
60424 } ma_dr_flac_vorbis_comment_iterator;
60425 MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments);
60426 MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut);
60427 typedef struct
60428 {
60429  ma_uint32 countRemaining;
60430  const char* pRunningData;
60431 } ma_dr_flac_cuesheet_track_iterator;
60432 typedef struct
60433 {
60434  ma_uint64 offset;
60435  ma_uint8 index;
60436  ma_uint8 reserved[3];
60437 } ma_dr_flac_cuesheet_track_index;
60438 typedef struct
60439 {
60440  ma_uint64 offset;
60441  ma_uint8 trackNumber;
60442  char ISRC[12];
60443  ma_bool8 isAudio;
60444  ma_bool8 preEmphasis;
60445  ma_uint8 indexCount;
60446  const ma_dr_flac_cuesheet_track_index* pIndexPoints;
60447 } ma_dr_flac_cuesheet_track;
60448 MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData);
60449 MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack);
60450 #ifdef __cplusplus
60451 }
60452 #endif
60453 #endif
60454 /* dr_flac_h end */
60455 #endif /* MA_NO_FLAC */
60456 
60457 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
60458 /* dr_mp3_h begin */
60459 #ifndef ma_dr_mp3_h
60460 #define ma_dr_mp3_h
60461 #ifdef __cplusplus
60462 extern "C" {
60463 #endif
60464 #define MA_DR_MP3_STRINGIFY(x) #x
60465 #define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x)
60466 #define MA_DR_MP3_VERSION_MAJOR 0
60467 #define MA_DR_MP3_VERSION_MINOR 6
60468 #define MA_DR_MP3_VERSION_REVISION 38
60469 #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)
60470 #include <stddef.h>
60471 #define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
60472 #define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
60473 MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
60474 MA_API const char* ma_dr_mp3_version_string(void);
60475 typedef struct
60476 {
60477  int frame_bytes, channels, hz, layer, bitrate_kbps;
60478 } ma_dr_mp3dec_frame_info;
60479 typedef struct
60480 {
60481  float mdct_overlap[2][9*32], qmf_state[15*2*32];
60482  int reserv, free_format_bytes;
60483  ma_uint8 header[4], reserv_buf[511];
60484 } ma_dr_mp3dec;
60485 MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec);
60486 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);
60487 MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples);
60488 typedef enum
60489 {
60490  ma_dr_mp3_seek_origin_start,
60491  ma_dr_mp3_seek_origin_current
60492 } ma_dr_mp3_seek_origin;
60493 typedef struct
60494 {
60495  ma_uint64 seekPosInBytes;
60496  ma_uint64 pcmFrameIndex;
60497  ma_uint16 mp3FramesToDiscard;
60498  ma_uint16 pcmFramesToDiscard;
60499 } ma_dr_mp3_seek_point;
60500 typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
60501 typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin);
60502 typedef struct
60503 {
60504  ma_uint32 channels;
60505  ma_uint32 sampleRate;
60506 } ma_dr_mp3_config;
60507 typedef struct
60508 {
60509  ma_dr_mp3dec decoder;
60510  ma_uint32 channels;
60511  ma_uint32 sampleRate;
60512  ma_dr_mp3_read_proc onRead;
60513  ma_dr_mp3_seek_proc onSeek;
60514  void* pUserData;
60515  ma_allocation_callbacks allocationCallbacks;
60516  ma_uint32 mp3FrameChannels;
60517  ma_uint32 mp3FrameSampleRate;
60518  ma_uint32 pcmFramesConsumedInMP3Frame;
60519  ma_uint32 pcmFramesRemainingInMP3Frame;
60520  ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME];
60521  ma_uint64 currentPCMFrame;
60522  ma_uint64 streamCursor;
60523  ma_dr_mp3_seek_point* pSeekPoints;
60524  ma_uint32 seekPointCount;
60525  size_t dataSize;
60526  size_t dataCapacity;
60527  size_t dataConsumed;
60528  ma_uint8* pData;
60529  ma_bool32 atEnd : 1;
60530  struct
60531  {
60532  const ma_uint8* pData;
60533  size_t dataSize;
60534  size_t currentReadPos;
60535  } memory;
60536 } ma_dr_mp3;
60537 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);
60538 MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
60539 #ifndef MA_DR_MP3_NO_STDIO
60540 MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks);
60541 MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks);
60542 #endif
60543 MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3);
60544 MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut);
60545 MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut);
60546 MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex);
60547 MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3);
60548 MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3);
60549 MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount);
60550 MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints);
60551 MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints);
60552 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);
60553 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);
60554 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);
60555 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);
60556 #ifndef MA_DR_MP3_NO_STDIO
60557 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);
60558 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);
60559 #endif
60560 MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
60561 MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
60562 #ifdef __cplusplus
60563 }
60564 #endif
60565 #endif
60566 /* dr_mp3_h end */
60567 #endif /* MA_NO_MP3 */
60568 
60569 
60570 /**************************************************************************************************************************************************************
60571 
60572 Decoding
60573 
60574 **************************************************************************************************************************************************************/
60575 #ifndef MA_NO_DECODING
60576 
60577 static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
60578 {
60579  MA_ASSERT(pDecoder != NULL);
60580 
60581  return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead);
60582 }
60583 
60584 static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
60585 {
60586  MA_ASSERT(pDecoder != NULL);
60587 
60588  return pDecoder->onSeek(pDecoder, byteOffset, origin);
60589 }
60590 
60591 static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor)
60592 {
60593  MA_ASSERT(pDecoder != NULL);
60594 
60595  if (pDecoder->onTell == NULL) {
60596  return MA_NOT_IMPLEMENTED;
60597  }
60598 
60599  return pDecoder->onTell(pDecoder, pCursor);
60600 }
60601 
60602 
60603 MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount)
60604 {
60606 
60607  MA_ZERO_OBJECT(&config);
60608  config.preferredFormat = preferredFormat;
60609  config.seekPointCount = seekPointCount;
60610 
60611  return config;
60612 }
60613 
60614 
60615 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
60616 {
60617  ma_decoder_config config;
60618  MA_ZERO_OBJECT(&config);
60619  config.format = outputFormat;
60620  config.channels = outputChannels;
60621  config.sampleRate = outputSampleRate;
60622  config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */
60623  config.encodingFormat = ma_encoding_format_unknown;
60624 
60625  /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
60626 
60627  return config;
60628 }
60629 
60630 MA_API ma_decoder_config ma_decoder_config_init_default()
60631 {
60632  return ma_decoder_config_init(ma_format_unknown, 0, 0);
60633 }
60634 
60635 MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
60636 {
60637  ma_decoder_config config;
60638  if (pConfig != NULL) {
60639  config = *pConfig;
60640  } else {
60641  MA_ZERO_OBJECT(&config);
60642  }
60643 
60644  return config;
60645 }
60646 
60647 static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
60648 {
60649  ma_result result;
60650  ma_data_converter_config converterConfig;
60651  ma_format internalFormat;
60652  ma_uint32 internalChannels;
60653  ma_uint32 internalSampleRate;
60654  ma_channel internalChannelMap[MA_MAX_CHANNELS];
60655 
60656  MA_ASSERT(pDecoder != NULL);
60657  MA_ASSERT(pConfig != NULL);
60658 
60659  result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap));
60660  if (result != MA_SUCCESS) {
60661  return result; /* Failed to retrieve the internal data format. */
60662  }
60663 
60664 
60665  /* Make sure we're not asking for too many channels. */
60666  if (pConfig->channels > MA_MAX_CHANNELS) {
60667  return MA_INVALID_ARGS;
60668  }
60669 
60670  /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
60671  if (internalChannels > MA_MAX_CHANNELS) {
60672  return MA_INVALID_ARGS;
60673  }
60674 
60675 
60676  /* Output format. */
60677  if (pConfig->format == ma_format_unknown) {
60678  pDecoder->outputFormat = internalFormat;
60679  } else {
60680  pDecoder->outputFormat = pConfig->format;
60681  }
60682 
60683  if (pConfig->channels == 0) {
60684  pDecoder->outputChannels = internalChannels;
60685  } else {
60686  pDecoder->outputChannels = pConfig->channels;
60687  }
60688 
60689  if (pConfig->sampleRate == 0) {
60690  pDecoder->outputSampleRate = internalSampleRate;
60691  } else {
60692  pDecoder->outputSampleRate = pConfig->sampleRate;
60693  }
60694 
60695  converterConfig = ma_data_converter_config_init(
60696  internalFormat, pDecoder->outputFormat,
60697  internalChannels, pDecoder->outputChannels,
60698  internalSampleRate, pDecoder->outputSampleRate
60699  );
60700  converterConfig.pChannelMapIn = internalChannelMap;
60701  converterConfig.pChannelMapOut = pConfig->pChannelMap;
60702  converterConfig.channelMixMode = pConfig->channelMixMode;
60703  converterConfig.ditherMode = pConfig->ditherMode;
60704  converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
60705  converterConfig.resampling = pConfig->resampling;
60706 
60707  result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter);
60708  if (result != MA_SUCCESS) {
60709  return result;
60710  }
60711 
60712  /*
60713  Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll
60714  need this if the data converter does not support calculation of the required input frame count. To
60715  determine support for this we'll just run a test.
60716  */
60717  {
60718  ma_uint64 unused;
60719 
60720  result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused);
60721  if (result != MA_SUCCESS) {
60722  /*
60723  We were unable to calculate the required input frame count which means we'll need to use
60724  a heap-allocated cache.
60725  */
60726  ma_uint64 inputCacheCapSizeInBytes;
60727 
60728  pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels);
60729 
60730  /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */
60731  inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels);
60732  if (inputCacheCapSizeInBytes > MA_SIZE_MAX) {
60733  ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
60734  return MA_OUT_OF_MEMORY;
60735  }
60736 
60737  pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */
60738  if (pDecoder->pInputCache == NULL) {
60739  ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
60740  return MA_OUT_OF_MEMORY;
60741  }
60742  }
60743  }
60744 
60745  return MA_SUCCESS;
60746 }
60747 
60748 
60749 
60750 static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
60751 {
60752  ma_decoder* pDecoder = (ma_decoder*)pUserData;
60753  MA_ASSERT(pDecoder != NULL);
60754 
60755  return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead);
60756 }
60757 
60758 static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin)
60759 {
60760  ma_decoder* pDecoder = (ma_decoder*)pUserData;
60761  MA_ASSERT(pDecoder != NULL);
60762 
60763  return ma_decoder_seek_bytes(pDecoder, offset, origin);
60764 }
60765 
60766 static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor)
60767 {
60768  ma_decoder* pDecoder = (ma_decoder*)pUserData;
60769  MA_ASSERT(pDecoder != NULL);
60770 
60771  return ma_decoder_tell_bytes(pDecoder, pCursor);
60772 }
60773 
60774 
60775 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)
60776 {
60777  ma_result result;
60778  ma_decoding_backend_config backendConfig;
60779  ma_data_source* pBackend;
60780 
60781  MA_ASSERT(pVTable != NULL);
60782  MA_ASSERT(pConfig != NULL);
60783  MA_ASSERT(pDecoder != NULL);
60784 
60785  if (pVTable->onInit == NULL) {
60786  return MA_NOT_IMPLEMENTED;
60787  }
60788 
60789  backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
60790 
60791  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);
60792  if (result != MA_SUCCESS) {
60793  return result; /* Failed to initialize the backend from this vtable. */
60794  }
60795 
60796  /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
60797  pDecoder->pBackend = pBackend;
60798  pDecoder->pBackendVTable = pVTable;
60799  pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
60800 
60801  return MA_SUCCESS;
60802 }
60803 
60804 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)
60805 {
60806  ma_result result;
60807  ma_decoding_backend_config backendConfig;
60808  ma_data_source* pBackend;
60809 
60810  MA_ASSERT(pVTable != NULL);
60811  MA_ASSERT(pConfig != NULL);
60812  MA_ASSERT(pDecoder != NULL);
60813 
60814  if (pVTable->onInitFile == NULL) {
60815  return MA_NOT_IMPLEMENTED;
60816  }
60817 
60818  backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
60819 
60820  result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
60821  if (result != MA_SUCCESS) {
60822  return result; /* Failed to initialize the backend from this vtable. */
60823  }
60824 
60825  /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
60826  pDecoder->pBackend = pBackend;
60827  pDecoder->pBackendVTable = pVTable;
60828  pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
60829 
60830  return MA_SUCCESS;
60831 }
60832 
60833 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)
60834 {
60835  ma_result result;
60836  ma_decoding_backend_config backendConfig;
60837  ma_data_source* pBackend;
60838 
60839  MA_ASSERT(pVTable != NULL);
60840  MA_ASSERT(pConfig != NULL);
60841  MA_ASSERT(pDecoder != NULL);
60842 
60843  if (pVTable->onInitFileW == NULL) {
60844  return MA_NOT_IMPLEMENTED;
60845  }
60846 
60847  backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
60848 
60849  result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
60850  if (result != MA_SUCCESS) {
60851  return result; /* Failed to initialize the backend from this vtable. */
60852  }
60853 
60854  /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
60855  pDecoder->pBackend = pBackend;
60856  pDecoder->pBackendVTable = pVTable;
60857  pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
60858 
60859  return MA_SUCCESS;
60860 }
60861 
60862 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)
60863 {
60864  ma_result result;
60865  ma_decoding_backend_config backendConfig;
60866  ma_data_source* pBackend;
60867 
60868  MA_ASSERT(pVTable != NULL);
60869  MA_ASSERT(pConfig != NULL);
60870  MA_ASSERT(pDecoder != NULL);
60871 
60872  if (pVTable->onInitMemory == NULL) {
60873  return MA_NOT_IMPLEMENTED;
60874  }
60875 
60876  backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
60877 
60878  result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
60879  if (result != MA_SUCCESS) {
60880  return result; /* Failed to initialize the backend from this vtable. */
60881  }
60882 
60883  /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
60884  pDecoder->pBackend = pBackend;
60885  pDecoder->pBackendVTable = pVTable;
60886  pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
60887 
60888  return MA_SUCCESS;
60889 }
60890 
60891 
60892 
60893 static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
60894 {
60895  ma_result result = MA_NO_BACKEND;
60896  size_t ivtable;
60897 
60898  MA_ASSERT(pConfig != NULL);
60899  MA_ASSERT(pDecoder != NULL);
60900 
60901  if (pConfig->ppCustomBackendVTables == NULL) {
60902  return MA_NO_BACKEND;
60903  }
60904 
60905  /* The order each backend is listed is what defines the priority. */
60906  for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
60907  const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
60908  if (pVTable != NULL) {
60909  result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder);
60910  if (result == MA_SUCCESS) {
60911  return MA_SUCCESS;
60912  } else {
60913  /* 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. */
60914  result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
60915  if (result != MA_SUCCESS) {
60916  return result; /* Failed to seek back to the start. */
60917  }
60918  }
60919  } else {
60920  /* No vtable. */
60921  }
60922  }
60923 
60924  /* Getting here means we couldn't find a backend. */
60925  return MA_NO_BACKEND;
60926 }
60927 
60928 static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
60929 {
60930  ma_result result = MA_NO_BACKEND;
60931  size_t ivtable;
60932 
60933  MA_ASSERT(pConfig != NULL);
60934  MA_ASSERT(pDecoder != NULL);
60935 
60936  if (pConfig->ppCustomBackendVTables == NULL) {
60937  return MA_NO_BACKEND;
60938  }
60939 
60940  /* The order each backend is listed is what defines the priority. */
60941  for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
60942  const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
60943  if (pVTable != NULL) {
60944  result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder);
60945  if (result == MA_SUCCESS) {
60946  return MA_SUCCESS;
60947  }
60948  } else {
60949  /* No vtable. */
60950  }
60951  }
60952 
60953  /* Getting here means we couldn't find a backend. */
60954  return MA_NO_BACKEND;
60955 }
60956 
60957 static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
60958 {
60959  ma_result result = MA_NO_BACKEND;
60960  size_t ivtable;
60961 
60962  MA_ASSERT(pConfig != NULL);
60963  MA_ASSERT(pDecoder != NULL);
60964 
60965  if (pConfig->ppCustomBackendVTables == NULL) {
60966  return MA_NO_BACKEND;
60967  }
60968 
60969  /* The order each backend is listed is what defines the priority. */
60970  for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
60971  const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
60972  if (pVTable != NULL) {
60973  result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder);
60974  if (result == MA_SUCCESS) {
60975  return MA_SUCCESS;
60976  }
60977  } else {
60978  /* No vtable. */
60979  }
60980  }
60981 
60982  /* Getting here means we couldn't find a backend. */
60983  return MA_NO_BACKEND;
60984 }
60985 
60986 static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
60987 {
60988  ma_result result = MA_NO_BACKEND;
60989  size_t ivtable;
60990 
60991  MA_ASSERT(pConfig != NULL);
60992  MA_ASSERT(pDecoder != NULL);
60993 
60994  if (pConfig->ppCustomBackendVTables == NULL) {
60995  return MA_NO_BACKEND;
60996  }
60997 
60998  /* The order each backend is listed is what defines the priority. */
60999  for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
61000  const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
61001  if (pVTable != NULL) {
61002  result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder);
61003  if (result == MA_SUCCESS) {
61004  return MA_SUCCESS;
61005  }
61006  } else {
61007  /* No vtable. */
61008  }
61009  }
61010 
61011  /* Getting here means we couldn't find a backend. */
61012  return MA_NO_BACKEND;
61013 }
61014 
61015 
61016 /* WAV */
61017 #ifdef ma_dr_wav_h
61018 #define MA_HAS_WAV
61019 
61020 typedef struct
61021 {
61023  ma_read_proc onRead;
61024  ma_seek_proc onSeek;
61025  ma_tell_proc onTell;
61026  void* pReadSeekTellUserData;
61027  ma_format format; /* Can be f32, s16 or s32. */
61028 #if !defined(MA_NO_WAV)
61029  ma_dr_wav dr;
61030 #endif
61031 } ma_wav;
61032 
61033 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);
61034 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);
61035 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);
61036 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);
61037 MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks);
61038 MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
61039 MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex);
61040 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);
61041 MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor);
61042 MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength);
61043 
61044 
61045 static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
61046 {
61047  return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead);
61048 }
61049 
61050 static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
61051 {
61052  return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex);
61053 }
61054 
61055 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)
61056 {
61057  return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
61058 }
61059 
61060 static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
61061 {
61062  return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor);
61063 }
61064 
61065 static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
61066 {
61067  return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength);
61068 }
61069 
61070 static ma_data_source_vtable g_ma_wav_ds_vtable =
61071 {
61072  ma_wav_ds_read,
61073  ma_wav_ds_seek,
61074  ma_wav_ds_get_data_format,
61075  ma_wav_ds_get_cursor,
61076  ma_wav_ds_get_length,
61077  NULL, /* onSetLooping */
61078  0
61079 };
61080 
61081 
61082 #if !defined(MA_NO_WAV)
61083 static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
61084 {
61085  ma_wav* pWav = (ma_wav*)pUserData;
61086  ma_result result;
61087  size_t bytesRead;
61088 
61089  MA_ASSERT(pWav != NULL);
61090 
61091  result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
61092  (void)result;
61093 
61094  return bytesRead;
61095 }
61096 
61097 static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
61098 {
61099  ma_wav* pWav = (ma_wav*)pUserData;
61100  ma_result result;
61101  ma_seek_origin maSeekOrigin;
61102 
61103  MA_ASSERT(pWav != NULL);
61104 
61105  maSeekOrigin = ma_seek_origin_start;
61106  if (origin == ma_dr_wav_seek_origin_current) {
61107  maSeekOrigin = ma_seek_origin_current;
61108  }
61109 
61110  result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin);
61111  if (result != MA_SUCCESS) {
61112  return MA_FALSE;
61113  }
61114 
61115  return MA_TRUE;
61116 }
61117 #endif
61118 
61119 static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav)
61120 {
61121  ma_result result;
61122  ma_data_source_config dataSourceConfig;
61123 
61124  if (pWav == NULL) {
61125  return MA_INVALID_ARGS;
61126  }
61127 
61128  MA_ZERO_OBJECT(pWav);
61129  pWav->format = ma_format_unknown; /* Use closest match to source file by default. */
61130 
61131  if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
61132  pWav->format = pConfig->preferredFormat;
61133  } else {
61134  /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
61135  }
61136 
61137  dataSourceConfig = ma_data_source_config_init();
61138  dataSourceConfig.vtable = &g_ma_wav_ds_vtable;
61139 
61140  result = ma_data_source_init(&dataSourceConfig, &pWav->ds);
61141  if (result != MA_SUCCESS) {
61142  return result; /* Failed to initialize the base data source. */
61143  }
61144 
61145  return MA_SUCCESS;
61146 }
61147 
61148 static ma_result ma_wav_post_init(ma_wav* pWav)
61149 {
61150  /*
61151  If an explicit format was not specified, try picking the closest match based on the internal
61152  format. The format needs to be supported by miniaudio.
61153  */
61154  if (pWav->format == ma_format_unknown) {
61155  switch (pWav->dr.translatedFormatTag)
61156  {
61157  case MA_DR_WAVE_FORMAT_PCM:
61158  {
61159  if (pWav->dr.bitsPerSample == 8) {
61160  pWav->format = ma_format_u8;
61161  } else if (pWav->dr.bitsPerSample == 16) {
61162  pWav->format = ma_format_s16;
61163  } else if (pWav->dr.bitsPerSample == 24) {
61164  pWav->format = ma_format_s24;
61165  } else if (pWav->dr.bitsPerSample == 32) {
61166  pWav->format = ma_format_s32;
61167  }
61168  } break;
61169 
61170  case MA_DR_WAVE_FORMAT_IEEE_FLOAT:
61171  {
61172  if (pWav->dr.bitsPerSample == 32) {
61173  pWav->format = ma_format_f32;
61174  }
61175  } break;
61176 
61177  default: break;
61178  }
61179 
61180  /* Fall back to f32 if we couldn't find anything. */
61181  if (pWav->format == ma_format_unknown) {
61182  pWav->format = ma_format_f32;
61183  }
61184  }
61185 
61186  return MA_SUCCESS;
61187 }
61188 
61189 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)
61190 {
61191  ma_result result;
61192 
61193  result = ma_wav_init_internal(pConfig, pWav);
61194  if (result != MA_SUCCESS) {
61195  return result;
61196  }
61197 
61198  if (onRead == NULL || onSeek == NULL) {
61199  return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
61200  }
61201 
61202  pWav->onRead = onRead;
61203  pWav->onSeek = onSeek;
61204  pWav->onTell = onTell;
61205  pWav->pReadSeekTellUserData = pReadSeekTellUserData;
61206 
61207  #if !defined(MA_NO_WAV)
61208  {
61209  ma_bool32 wavResult;
61210 
61211  wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks);
61212  if (wavResult != MA_TRUE) {
61213  return MA_INVALID_FILE;
61214  }
61215 
61216  ma_wav_post_init(pWav);
61217 
61218  return MA_SUCCESS;
61219  }
61220  #else
61221  {
61222  /* wav is disabled. */
61223  (void)pAllocationCallbacks;
61224  return MA_NOT_IMPLEMENTED;
61225  }
61226  #endif
61227 }
61228 
61229 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)
61230 {
61231  ma_result result;
61232 
61233  result = ma_wav_init_internal(pConfig, pWav);
61234  if (result != MA_SUCCESS) {
61235  return result;
61236  }
61237 
61238  #if !defined(MA_NO_WAV)
61239  {
61240  ma_bool32 wavResult;
61241 
61242  wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks);
61243  if (wavResult != MA_TRUE) {
61244  return MA_INVALID_FILE;
61245  }
61246 
61247  ma_wav_post_init(pWav);
61248 
61249  return MA_SUCCESS;
61250  }
61251  #else
61252  {
61253  /* wav is disabled. */
61254  (void)pFilePath;
61255  (void)pAllocationCallbacks;
61256  return MA_NOT_IMPLEMENTED;
61257  }
61258  #endif
61259 }
61260 
61261 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)
61262 {
61263  ma_result result;
61264 
61265  result = ma_wav_init_internal(pConfig, pWav);
61266  if (result != MA_SUCCESS) {
61267  return result;
61268  }
61269 
61270  #if !defined(MA_NO_WAV)
61271  {
61272  ma_bool32 wavResult;
61273 
61274  wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks);
61275  if (wavResult != MA_TRUE) {
61276  return MA_INVALID_FILE;
61277  }
61278 
61279  ma_wav_post_init(pWav);
61280 
61281  return MA_SUCCESS;
61282  }
61283  #else
61284  {
61285  /* wav is disabled. */
61286  (void)pFilePath;
61287  (void)pAllocationCallbacks;
61288  return MA_NOT_IMPLEMENTED;
61289  }
61290  #endif
61291 }
61292 
61293 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)
61294 {
61295  ma_result result;
61296 
61297  result = ma_wav_init_internal(pConfig, pWav);
61298  if (result != MA_SUCCESS) {
61299  return result;
61300  }
61301 
61302  #if !defined(MA_NO_WAV)
61303  {
61304  ma_bool32 wavResult;
61305 
61306  wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks);
61307  if (wavResult != MA_TRUE) {
61308  return MA_INVALID_FILE;
61309  }
61310 
61311  ma_wav_post_init(pWav);
61312 
61313  return MA_SUCCESS;
61314  }
61315  #else
61316  {
61317  /* wav is disabled. */
61318  (void)pData;
61319  (void)dataSize;
61320  (void)pAllocationCallbacks;
61321  return MA_NOT_IMPLEMENTED;
61322  }
61323  #endif
61324 }
61325 
61326 MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks)
61327 {
61328  if (pWav == NULL) {
61329  return;
61330  }
61331 
61332  (void)pAllocationCallbacks;
61333 
61334  #if !defined(MA_NO_WAV)
61335  {
61336  ma_dr_wav_uninit(&pWav->dr);
61337  }
61338  #else
61339  {
61340  /* wav is disabled. Should never hit this since initialization would have failed. */
61341  MA_ASSERT(MA_FALSE);
61342  }
61343  #endif
61344 
61345  ma_data_source_uninit(&pWav->ds);
61346 }
61347 
61348 MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
61349 {
61350  if (pFramesRead != NULL) {
61351  *pFramesRead = 0;
61352  }
61353 
61354  if (frameCount == 0) {
61355  return MA_INVALID_ARGS;
61356  }
61357 
61358  if (pWav == NULL) {
61359  return MA_INVALID_ARGS;
61360  }
61361 
61362  #if !defined(MA_NO_WAV)
61363  {
61364  /* We always use floating point format. */
61365  ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
61366  ma_uint64 totalFramesRead = 0;
61367  ma_format format;
61368 
61369  ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0);
61370 
61371  switch (format)
61372  {
61373  case ma_format_f32:
61374  {
61375  totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut);
61376  } break;
61377 
61378  case ma_format_s16:
61379  {
61380  totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut);
61381  } break;
61382 
61383  case ma_format_s32:
61384  {
61385  totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut);
61386  } break;
61387 
61388  /* Fallback to a raw read. */
61389  case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */
61390  default:
61391  {
61392  totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut);
61393  } break;
61394  }
61395 
61396  /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */
61397  if (totalFramesRead == 0) {
61398  result = MA_AT_END;
61399  }
61400 
61401  if (pFramesRead != NULL) {
61402  *pFramesRead = totalFramesRead;
61403  }
61404 
61405  if (result == MA_SUCCESS && totalFramesRead == 0) {
61406  result = MA_AT_END;
61407  }
61408 
61409  return result;
61410  }
61411  #else
61412  {
61413  /* wav is disabled. Should never hit this since initialization would have failed. */
61414  MA_ASSERT(MA_FALSE);
61415 
61416  (void)pFramesOut;
61417  (void)frameCount;
61418  (void)pFramesRead;
61419 
61420  return MA_NOT_IMPLEMENTED;
61421  }
61422  #endif
61423 }
61424 
61425 MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex)
61426 {
61427  if (pWav == NULL) {
61428  return MA_INVALID_ARGS;
61429  }
61430 
61431  #if !defined(MA_NO_WAV)
61432  {
61433  ma_bool32 wavResult;
61434 
61435  wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex);
61436  if (wavResult != MA_TRUE) {
61437  return MA_ERROR;
61438  }
61439 
61440  return MA_SUCCESS;
61441  }
61442  #else
61443  {
61444  /* wav is disabled. Should never hit this since initialization would have failed. */
61445  MA_ASSERT(MA_FALSE);
61446 
61447  (void)frameIndex;
61448 
61449  return MA_NOT_IMPLEMENTED;
61450  }
61451  #endif
61452 }
61453 
61454 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)
61455 {
61456  /* Defaults for safety. */
61457  if (pFormat != NULL) {
61458  *pFormat = ma_format_unknown;
61459  }
61460  if (pChannels != NULL) {
61461  *pChannels = 0;
61462  }
61463  if (pSampleRate != NULL) {
61464  *pSampleRate = 0;
61465  }
61466  if (pChannelMap != NULL) {
61467  MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
61468  }
61469 
61470  if (pWav == NULL) {
61471  return MA_INVALID_OPERATION;
61472  }
61473 
61474  if (pFormat != NULL) {
61475  *pFormat = pWav->format;
61476  }
61477 
61478  #if !defined(MA_NO_WAV)
61479  {
61480  if (pChannels != NULL) {
61481  *pChannels = pWav->dr.channels;
61482  }
61483 
61484  if (pSampleRate != NULL) {
61485  *pSampleRate = pWav->dr.sampleRate;
61486  }
61487 
61488  if (pChannelMap != NULL) {
61489  ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels);
61490  }
61491 
61492  return MA_SUCCESS;
61493  }
61494  #else
61495  {
61496  /* wav is disabled. Should never hit this since initialization would have failed. */
61497  MA_ASSERT(MA_FALSE);
61498  return MA_NOT_IMPLEMENTED;
61499  }
61500  #endif
61501 }
61502 
61503 MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor)
61504 {
61505  if (pCursor == NULL) {
61506  return MA_INVALID_ARGS;
61507  }
61508 
61509  *pCursor = 0; /* Safety. */
61510 
61511  if (pWav == NULL) {
61512  return MA_INVALID_ARGS;
61513  }
61514 
61515  #if !defined(MA_NO_WAV)
61516  {
61517  ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor);
61518  if (wavResult != MA_SUCCESS) {
61519  return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */
61520  }
61521 
61522  return MA_SUCCESS;
61523  }
61524  #else
61525  {
61526  /* wav is disabled. Should never hit this since initialization would have failed. */
61527  MA_ASSERT(MA_FALSE);
61528  return MA_NOT_IMPLEMENTED;
61529  }
61530  #endif
61531 }
61532 
61533 MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength)
61534 {
61535  if (pLength == NULL) {
61536  return MA_INVALID_ARGS;
61537  }
61538 
61539  *pLength = 0; /* Safety. */
61540 
61541  if (pWav == NULL) {
61542  return MA_INVALID_ARGS;
61543  }
61544 
61545  #if !defined(MA_NO_WAV)
61546  {
61547  ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength);
61548  if (wavResult != MA_SUCCESS) {
61549  return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */
61550  }
61551 
61552  return MA_SUCCESS;
61553  }
61554  #else
61555  {
61556  /* wav is disabled. Should never hit this since initialization would have failed. */
61557  MA_ASSERT(MA_FALSE);
61558  return MA_NOT_IMPLEMENTED;
61559  }
61560  #endif
61561 }
61562 
61563 
61564 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)
61565 {
61566  ma_result result;
61567  ma_wav* pWav;
61568 
61569  (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. */
61570 
61571  /* For now we're just allocating the decoder backend on the heap. */
61572  pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
61573  if (pWav == NULL) {
61574  return MA_OUT_OF_MEMORY;
61575  }
61576 
61577  result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav);
61578  if (result != MA_SUCCESS) {
61579  ma_free(pWav, pAllocationCallbacks);
61580  return result;
61581  }
61582 
61583  *ppBackend = pWav;
61584 
61585  return MA_SUCCESS;
61586 }
61587 
61588 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)
61589 {
61590  ma_result result;
61591  ma_wav* pWav;
61592 
61593  (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. */
61594 
61595  /* For now we're just allocating the decoder backend on the heap. */
61596  pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
61597  if (pWav == NULL) {
61598  return MA_OUT_OF_MEMORY;
61599  }
61600 
61601  result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav);
61602  if (result != MA_SUCCESS) {
61603  ma_free(pWav, pAllocationCallbacks);
61604  return result;
61605  }
61606 
61607  *ppBackend = pWav;
61608 
61609  return MA_SUCCESS;
61610 }
61611 
61612 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)
61613 {
61614  ma_result result;
61615  ma_wav* pWav;
61616 
61617  (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. */
61618 
61619  /* For now we're just allocating the decoder backend on the heap. */
61620  pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
61621  if (pWav == NULL) {
61622  return MA_OUT_OF_MEMORY;
61623  }
61624 
61625  result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav);
61626  if (result != MA_SUCCESS) {
61627  ma_free(pWav, pAllocationCallbacks);
61628  return result;
61629  }
61630 
61631  *ppBackend = pWav;
61632 
61633  return MA_SUCCESS;
61634 }
61635 
61636 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)
61637 {
61638  ma_result result;
61639  ma_wav* pWav;
61640 
61641  (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. */
61642 
61643  /* For now we're just allocating the decoder backend on the heap. */
61644  pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
61645  if (pWav == NULL) {
61646  return MA_OUT_OF_MEMORY;
61647  }
61648 
61649  result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav);
61650  if (result != MA_SUCCESS) {
61651  ma_free(pWav, pAllocationCallbacks);
61652  return result;
61653  }
61654 
61655  *ppBackend = pWav;
61656 
61657  return MA_SUCCESS;
61658 }
61659 
61660 static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
61661 {
61662  ma_wav* pWav = (ma_wav*)pBackend;
61663 
61664  (void)pUserData;
61665 
61666  ma_wav_uninit(pWav, pAllocationCallbacks);
61667  ma_free(pWav, pAllocationCallbacks);
61668 }
61669 
61670 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav =
61671 {
61672  ma_decoding_backend_init__wav,
61673  ma_decoding_backend_init_file__wav,
61674  ma_decoding_backend_init_file_w__wav,
61675  ma_decoding_backend_init_memory__wav,
61676  ma_decoding_backend_uninit__wav
61677 };
61678 
61679 static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61680 {
61681  return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder);
61682 }
61683 
61684 static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61685 {
61686  return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder);
61687 }
61688 
61689 static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61690 {
61691  return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder);
61692 }
61693 
61694 static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61695 {
61696  return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder);
61697 }
61698 #endif /* ma_dr_wav_h */
61699 
61700 /* FLAC */
61701 #ifdef ma_dr_flac_h
61702 #define MA_HAS_FLAC
61703 
61704 typedef struct
61705 {
61707  ma_read_proc onRead;
61708  ma_seek_proc onSeek;
61709  ma_tell_proc onTell;
61710  void* pReadSeekTellUserData;
61711  ma_format format; /* Can be f32, s16 or s32. */
61712 #if !defined(MA_NO_FLAC)
61713  ma_dr_flac* dr;
61714 #endif
61715 } ma_flac;
61716 
61717 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);
61718 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);
61719 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);
61720 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);
61721 MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks);
61722 MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
61723 MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex);
61724 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);
61725 MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor);
61726 MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength);
61727 
61728 
61729 static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
61730 {
61731  return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead);
61732 }
61733 
61734 static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
61735 {
61736  return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex);
61737 }
61738 
61739 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)
61740 {
61741  return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
61742 }
61743 
61744 static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
61745 {
61746  return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor);
61747 }
61748 
61749 static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
61750 {
61751  return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength);
61752 }
61753 
61754 static ma_data_source_vtable g_ma_flac_ds_vtable =
61755 {
61756  ma_flac_ds_read,
61757  ma_flac_ds_seek,
61758  ma_flac_ds_get_data_format,
61759  ma_flac_ds_get_cursor,
61760  ma_flac_ds_get_length,
61761  NULL, /* onSetLooping */
61762  0
61763 };
61764 
61765 
61766 #if !defined(MA_NO_FLAC)
61767 static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
61768 {
61769  ma_flac* pFlac = (ma_flac*)pUserData;
61770  ma_result result;
61771  size_t bytesRead;
61772 
61773  MA_ASSERT(pFlac != NULL);
61774 
61775  result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
61776  (void)result;
61777 
61778  return bytesRead;
61779 }
61780 
61781 static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
61782 {
61783  ma_flac* pFlac = (ma_flac*)pUserData;
61784  ma_result result;
61785  ma_seek_origin maSeekOrigin;
61786 
61787  MA_ASSERT(pFlac != NULL);
61788 
61789  maSeekOrigin = ma_seek_origin_start;
61790  if (origin == ma_dr_flac_seek_origin_current) {
61791  maSeekOrigin = ma_seek_origin_current;
61792  }
61793 
61794  result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin);
61795  if (result != MA_SUCCESS) {
61796  return MA_FALSE;
61797  }
61798 
61799  return MA_TRUE;
61800 }
61801 #endif
61802 
61803 static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac)
61804 {
61805  ma_result result;
61806  ma_data_source_config dataSourceConfig;
61807 
61808  if (pFlac == NULL) {
61809  return MA_INVALID_ARGS;
61810  }
61811 
61812  MA_ZERO_OBJECT(pFlac);
61813  pFlac->format = ma_format_f32; /* f32 by default. */
61814 
61815  if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
61816  pFlac->format = pConfig->preferredFormat;
61817  } else {
61818  /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
61819  }
61820 
61821  dataSourceConfig = ma_data_source_config_init();
61822  dataSourceConfig.vtable = &g_ma_flac_ds_vtable;
61823 
61824  result = ma_data_source_init(&dataSourceConfig, &pFlac->ds);
61825  if (result != MA_SUCCESS) {
61826  return result; /* Failed to initialize the base data source. */
61827  }
61828 
61829  return MA_SUCCESS;
61830 }
61831 
61832 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)
61833 {
61834  ma_result result;
61835 
61836  result = ma_flac_init_internal(pConfig, pFlac);
61837  if (result != MA_SUCCESS) {
61838  return result;
61839  }
61840 
61841  if (onRead == NULL || onSeek == NULL) {
61842  return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
61843  }
61844 
61845  pFlac->onRead = onRead;
61846  pFlac->onSeek = onSeek;
61847  pFlac->onTell = onTell;
61848  pFlac->pReadSeekTellUserData = pReadSeekTellUserData;
61849 
61850  #if !defined(MA_NO_FLAC)
61851  {
61852  pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks);
61853  if (pFlac->dr == NULL) {
61854  return MA_INVALID_FILE;
61855  }
61856 
61857  return MA_SUCCESS;
61858  }
61859  #else
61860  {
61861  /* flac is disabled. */
61862  (void)pAllocationCallbacks;
61863  return MA_NOT_IMPLEMENTED;
61864  }
61865  #endif
61866 }
61867 
61868 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)
61869 {
61870  ma_result result;
61871 
61872  result = ma_flac_init_internal(pConfig, pFlac);
61873  if (result != MA_SUCCESS) {
61874  return result;
61875  }
61876 
61877  #if !defined(MA_NO_FLAC)
61878  {
61879  pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks);
61880  if (pFlac->dr == NULL) {
61881  return MA_INVALID_FILE;
61882  }
61883 
61884  return MA_SUCCESS;
61885  }
61886  #else
61887  {
61888  /* flac is disabled. */
61889  (void)pFilePath;
61890  (void)pAllocationCallbacks;
61891  return MA_NOT_IMPLEMENTED;
61892  }
61893  #endif
61894 }
61895 
61896 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)
61897 {
61898  ma_result result;
61899 
61900  result = ma_flac_init_internal(pConfig, pFlac);
61901  if (result != MA_SUCCESS) {
61902  return result;
61903  }
61904 
61905  #if !defined(MA_NO_FLAC)
61906  {
61907  pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks);
61908  if (pFlac->dr == NULL) {
61909  return MA_INVALID_FILE;
61910  }
61911 
61912  return MA_SUCCESS;
61913  }
61914  #else
61915  {
61916  /* flac is disabled. */
61917  (void)pFilePath;
61918  (void)pAllocationCallbacks;
61919  return MA_NOT_IMPLEMENTED;
61920  }
61921  #endif
61922 }
61923 
61924 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)
61925 {
61926  ma_result result;
61927 
61928  result = ma_flac_init_internal(pConfig, pFlac);
61929  if (result != MA_SUCCESS) {
61930  return result;
61931  }
61932 
61933  #if !defined(MA_NO_FLAC)
61934  {
61935  pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks);
61936  if (pFlac->dr == NULL) {
61937  return MA_INVALID_FILE;
61938  }
61939 
61940  return MA_SUCCESS;
61941  }
61942  #else
61943  {
61944  /* flac is disabled. */
61945  (void)pData;
61946  (void)dataSize;
61947  (void)pAllocationCallbacks;
61948  return MA_NOT_IMPLEMENTED;
61949  }
61950  #endif
61951 }
61952 
61953 MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks)
61954 {
61955  if (pFlac == NULL) {
61956  return;
61957  }
61958 
61959  (void)pAllocationCallbacks;
61960 
61961  #if !defined(MA_NO_FLAC)
61962  {
61963  ma_dr_flac_close(pFlac->dr);
61964  }
61965  #else
61966  {
61967  /* flac is disabled. Should never hit this since initialization would have failed. */
61968  MA_ASSERT(MA_FALSE);
61969  }
61970  #endif
61971 
61972  ma_data_source_uninit(&pFlac->ds);
61973 }
61974 
61975 MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
61976 {
61977  if (pFramesRead != NULL) {
61978  *pFramesRead = 0;
61979  }
61980 
61981  if (frameCount == 0) {
61982  return MA_INVALID_ARGS;
61983  }
61984 
61985  if (pFlac == NULL) {
61986  return MA_INVALID_ARGS;
61987  }
61988 
61989  #if !defined(MA_NO_FLAC)
61990  {
61991  /* We always use floating point format. */
61992  ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
61993  ma_uint64 totalFramesRead = 0;
61994  ma_format format;
61995 
61996  ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0);
61997 
61998  switch (format)
61999  {
62000  case ma_format_f32:
62001  {
62002  totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut);
62003  } break;
62004 
62005  case ma_format_s16:
62006  {
62007  totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut);
62008  } break;
62009 
62010  case ma_format_s32:
62011  {
62012  totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut);
62013  } break;
62014 
62015  case ma_format_u8:
62016  case ma_format_s24:
62017  case ma_format_unknown:
62018  default:
62019  {
62020  return MA_INVALID_OPERATION;
62021  };
62022  }
62023 
62024  /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */
62025  if (totalFramesRead == 0) {
62026  result = MA_AT_END;
62027  }
62028 
62029  if (pFramesRead != NULL) {
62030  *pFramesRead = totalFramesRead;
62031  }
62032 
62033  if (result == MA_SUCCESS && totalFramesRead == 0) {
62034  result = MA_AT_END;
62035  }
62036 
62037  return result;
62038  }
62039  #else
62040  {
62041  /* flac is disabled. Should never hit this since initialization would have failed. */
62042  MA_ASSERT(MA_FALSE);
62043 
62044  (void)pFramesOut;
62045  (void)frameCount;
62046  (void)pFramesRead;
62047 
62048  return MA_NOT_IMPLEMENTED;
62049  }
62050  #endif
62051 }
62052 
62053 MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex)
62054 {
62055  if (pFlac == NULL) {
62056  return MA_INVALID_ARGS;
62057  }
62058 
62059  #if !defined(MA_NO_FLAC)
62060  {
62061  ma_bool32 flacResult;
62062 
62063  flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex);
62064  if (flacResult != MA_TRUE) {
62065  return MA_ERROR;
62066  }
62067 
62068  return MA_SUCCESS;
62069  }
62070  #else
62071  {
62072  /* flac is disabled. Should never hit this since initialization would have failed. */
62073  MA_ASSERT(MA_FALSE);
62074 
62075  (void)frameIndex;
62076 
62077  return MA_NOT_IMPLEMENTED;
62078  }
62079  #endif
62080 }
62081 
62082 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)
62083 {
62084  /* Defaults for safety. */
62085  if (pFormat != NULL) {
62086  *pFormat = ma_format_unknown;
62087  }
62088  if (pChannels != NULL) {
62089  *pChannels = 0;
62090  }
62091  if (pSampleRate != NULL) {
62092  *pSampleRate = 0;
62093  }
62094  if (pChannelMap != NULL) {
62095  MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
62096  }
62097 
62098  if (pFlac == NULL) {
62099  return MA_INVALID_OPERATION;
62100  }
62101 
62102  if (pFormat != NULL) {
62103  *pFormat = pFlac->format;
62104  }
62105 
62106  #if !defined(MA_NO_FLAC)
62107  {
62108  if (pChannels != NULL) {
62109  *pChannels = pFlac->dr->channels;
62110  }
62111 
62112  if (pSampleRate != NULL) {
62113  *pSampleRate = pFlac->dr->sampleRate;
62114  }
62115 
62116  if (pChannelMap != NULL) {
62117  ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels);
62118  }
62119 
62120  return MA_SUCCESS;
62121  }
62122  #else
62123  {
62124  /* flac is disabled. Should never hit this since initialization would have failed. */
62125  MA_ASSERT(MA_FALSE);
62126  return MA_NOT_IMPLEMENTED;
62127  }
62128  #endif
62129 }
62130 
62131 MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor)
62132 {
62133  if (pCursor == NULL) {
62134  return MA_INVALID_ARGS;
62135  }
62136 
62137  *pCursor = 0; /* Safety. */
62138 
62139  if (pFlac == NULL) {
62140  return MA_INVALID_ARGS;
62141  }
62142 
62143  #if !defined(MA_NO_FLAC)
62144  {
62145  *pCursor = pFlac->dr->currentPCMFrame;
62146 
62147  return MA_SUCCESS;
62148  }
62149  #else
62150  {
62151  /* flac is disabled. Should never hit this since initialization would have failed. */
62152  MA_ASSERT(MA_FALSE);
62153  return MA_NOT_IMPLEMENTED;
62154  }
62155  #endif
62156 }
62157 
62158 MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength)
62159 {
62160  if (pLength == NULL) {
62161  return MA_INVALID_ARGS;
62162  }
62163 
62164  *pLength = 0; /* Safety. */
62165 
62166  if (pFlac == NULL) {
62167  return MA_INVALID_ARGS;
62168  }
62169 
62170  #if !defined(MA_NO_FLAC)
62171  {
62172  *pLength = pFlac->dr->totalPCMFrameCount;
62173 
62174  return MA_SUCCESS;
62175  }
62176  #else
62177  {
62178  /* flac is disabled. Should never hit this since initialization would have failed. */
62179  MA_ASSERT(MA_FALSE);
62180  return MA_NOT_IMPLEMENTED;
62181  }
62182  #endif
62183 }
62184 
62185 
62186 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)
62187 {
62188  ma_result result;
62189  ma_flac* pFlac;
62190 
62191  (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. */
62192 
62193  /* For now we're just allocating the decoder backend on the heap. */
62194  pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
62195  if (pFlac == NULL) {
62196  return MA_OUT_OF_MEMORY;
62197  }
62198 
62199  result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac);
62200  if (result != MA_SUCCESS) {
62201  ma_free(pFlac, pAllocationCallbacks);
62202  return result;
62203  }
62204 
62205  *ppBackend = pFlac;
62206 
62207  return MA_SUCCESS;
62208 }
62209 
62210 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)
62211 {
62212  ma_result result;
62213  ma_flac* pFlac;
62214 
62215  (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. */
62216 
62217  /* For now we're just allocating the decoder backend on the heap. */
62218  pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
62219  if (pFlac == NULL) {
62220  return MA_OUT_OF_MEMORY;
62221  }
62222 
62223  result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac);
62224  if (result != MA_SUCCESS) {
62225  ma_free(pFlac, pAllocationCallbacks);
62226  return result;
62227  }
62228 
62229  *ppBackend = pFlac;
62230 
62231  return MA_SUCCESS;
62232 }
62233 
62234 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)
62235 {
62236  ma_result result;
62237  ma_flac* pFlac;
62238 
62239  (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. */
62240 
62241  /* For now we're just allocating the decoder backend on the heap. */
62242  pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
62243  if (pFlac == NULL) {
62244  return MA_OUT_OF_MEMORY;
62245  }
62246 
62247  result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac);
62248  if (result != MA_SUCCESS) {
62249  ma_free(pFlac, pAllocationCallbacks);
62250  return result;
62251  }
62252 
62253  *ppBackend = pFlac;
62254 
62255  return MA_SUCCESS;
62256 }
62257 
62258 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)
62259 {
62260  ma_result result;
62261  ma_flac* pFlac;
62262 
62263  (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. */
62264 
62265  /* For now we're just allocating the decoder backend on the heap. */
62266  pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
62267  if (pFlac == NULL) {
62268  return MA_OUT_OF_MEMORY;
62269  }
62270 
62271  result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac);
62272  if (result != MA_SUCCESS) {
62273  ma_free(pFlac, pAllocationCallbacks);
62274  return result;
62275  }
62276 
62277  *ppBackend = pFlac;
62278 
62279  return MA_SUCCESS;
62280 }
62281 
62282 static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
62283 {
62284  ma_flac* pFlac = (ma_flac*)pBackend;
62285 
62286  (void)pUserData;
62287 
62288  ma_flac_uninit(pFlac, pAllocationCallbacks);
62289  ma_free(pFlac, pAllocationCallbacks);
62290 }
62291 
62292 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac =
62293 {
62294  ma_decoding_backend_init__flac,
62295  ma_decoding_backend_init_file__flac,
62296  ma_decoding_backend_init_file_w__flac,
62297  ma_decoding_backend_init_memory__flac,
62298  ma_decoding_backend_uninit__flac
62299 };
62300 
62301 static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62302 {
62303  return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder);
62304 }
62305 
62306 static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62307 {
62308  return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder);
62309 }
62310 
62311 static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62312 {
62313  return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder);
62314 }
62315 
62316 static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62317 {
62318  return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder);
62319 }
62320 #endif /* ma_dr_flac_h */
62321 
62322 /* MP3 */
62323 #ifdef ma_dr_mp3_h
62324 #define MA_HAS_MP3
62325 
62326 typedef struct
62327 {
62329  ma_read_proc onRead;
62330  ma_seek_proc onSeek;
62331  ma_tell_proc onTell;
62332  void* pReadSeekTellUserData;
62333  ma_format format; /* Can be f32 or s16. */
62334 #if !defined(MA_NO_MP3)
62335  ma_dr_mp3 dr;
62336  ma_uint32 seekPointCount;
62337  ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */
62338 #endif
62339 } ma_mp3;
62340 
62341 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);
62342 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);
62343 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);
62344 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);
62345 MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks);
62346 MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
62347 MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex);
62348 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);
62349 MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor);
62350 MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength);
62351 
62352 
62353 static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
62354 {
62355  return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead);
62356 }
62357 
62358 static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
62359 {
62360  return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex);
62361 }
62362 
62363 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)
62364 {
62365  return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
62366 }
62367 
62368 static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
62369 {
62370  return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor);
62371 }
62372 
62373 static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
62374 {
62375  return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength);
62376 }
62377 
62378 static ma_data_source_vtable g_ma_mp3_ds_vtable =
62379 {
62380  ma_mp3_ds_read,
62381  ma_mp3_ds_seek,
62382  ma_mp3_ds_get_data_format,
62383  ma_mp3_ds_get_cursor,
62384  ma_mp3_ds_get_length,
62385  NULL, /* onSetLooping */
62386  0
62387 };
62388 
62389 
62390 #if !defined(MA_NO_MP3)
62391 static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
62392 {
62393  ma_mp3* pMP3 = (ma_mp3*)pUserData;
62394  ma_result result;
62395  size_t bytesRead;
62396 
62397  MA_ASSERT(pMP3 != NULL);
62398 
62399  result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
62400  (void)result;
62401 
62402  return bytesRead;
62403 }
62404 
62405 static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin)
62406 {
62407  ma_mp3* pMP3 = (ma_mp3*)pUserData;
62408  ma_result result;
62409  ma_seek_origin maSeekOrigin;
62410 
62411  MA_ASSERT(pMP3 != NULL);
62412 
62413  maSeekOrigin = ma_seek_origin_start;
62414  if (origin == ma_dr_mp3_seek_origin_current) {
62415  maSeekOrigin = ma_seek_origin_current;
62416  }
62417 
62418  result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin);
62419  if (result != MA_SUCCESS) {
62420  return MA_FALSE;
62421  }
62422 
62423  return MA_TRUE;
62424 }
62425 #endif
62426 
62427 static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3)
62428 {
62429  ma_result result;
62430  ma_data_source_config dataSourceConfig;
62431 
62432  if (pMP3 == NULL) {
62433  return MA_INVALID_ARGS;
62434  }
62435 
62436  MA_ZERO_OBJECT(pMP3);
62437  pMP3->format = ma_format_f32; /* f32 by default. */
62438 
62439  if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
62440  pMP3->format = pConfig->preferredFormat;
62441  } else {
62442  /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
62443  }
62444 
62445  dataSourceConfig = ma_data_source_config_init();
62446  dataSourceConfig.vtable = &g_ma_mp3_ds_vtable;
62447 
62448  result = ma_data_source_init(&dataSourceConfig, &pMP3->ds);
62449  if (result != MA_SUCCESS) {
62450  return result; /* Failed to initialize the base data source. */
62451  }
62452 
62453  return MA_SUCCESS;
62454 }
62455 
62456 static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)
62457 {
62458  ma_bool32 mp3Result;
62459  ma_uint32 seekPointCount = 0;
62460  ma_dr_mp3_seek_point* pSeekPoints = NULL;
62461 
62462  MA_ASSERT(pMP3 != NULL);
62463  MA_ASSERT(pConfig != NULL);
62464 
62465  seekPointCount = pConfig->seekPointCount;
62466  if (seekPointCount > 0) {
62467  pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks);
62468  if (pSeekPoints == NULL) {
62469  return MA_OUT_OF_MEMORY;
62470  }
62471  }
62472 
62473  mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints);
62474  if (mp3Result != MA_TRUE) {
62475  ma_free(pSeekPoints, pAllocationCallbacks);
62476  return MA_ERROR;
62477  }
62478 
62479  mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints);
62480  if (mp3Result != MA_TRUE) {
62481  ma_free(pSeekPoints, pAllocationCallbacks);
62482  return MA_ERROR;
62483  }
62484 
62485  pMP3->seekPointCount = seekPointCount;
62486  pMP3->pSeekPoints = pSeekPoints;
62487 
62488  return MA_SUCCESS;
62489 }
62490 
62491 static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)
62492 {
62493  ma_result result;
62494 
62495  result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
62496  if (result != MA_SUCCESS) {
62497  return result;
62498  }
62499 
62500  return MA_SUCCESS;
62501 }
62502 
62503 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)
62504 {
62505  ma_result result;
62506 
62507  result = ma_mp3_init_internal(pConfig, pMP3);
62508  if (result != MA_SUCCESS) {
62509  return result;
62510  }
62511 
62512  if (onRead == NULL || onSeek == NULL) {
62513  return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
62514  }
62515 
62516  pMP3->onRead = onRead;
62517  pMP3->onSeek = onSeek;
62518  pMP3->onTell = onTell;
62519  pMP3->pReadSeekTellUserData = pReadSeekTellUserData;
62520 
62521  #if !defined(MA_NO_MP3)
62522  {
62523  ma_bool32 mp3Result;
62524 
62525  mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks);
62526  if (mp3Result != MA_TRUE) {
62527  return MA_INVALID_FILE;
62528  }
62529 
62530  ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
62531 
62532  return MA_SUCCESS;
62533  }
62534  #else
62535  {
62536  /* mp3 is disabled. */
62537  (void)pAllocationCallbacks;
62538  return MA_NOT_IMPLEMENTED;
62539  }
62540  #endif
62541 }
62542 
62543 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)
62544 {
62545  ma_result result;
62546 
62547  result = ma_mp3_init_internal(pConfig, pMP3);
62548  if (result != MA_SUCCESS) {
62549  return result;
62550  }
62551 
62552  #if !defined(MA_NO_MP3)
62553  {
62554  ma_bool32 mp3Result;
62555 
62556  mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks);
62557  if (mp3Result != MA_TRUE) {
62558  return MA_INVALID_FILE;
62559  }
62560 
62561  ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
62562 
62563  return MA_SUCCESS;
62564  }
62565  #else
62566  {
62567  /* mp3 is disabled. */
62568  (void)pFilePath;
62569  (void)pAllocationCallbacks;
62570  return MA_NOT_IMPLEMENTED;
62571  }
62572  #endif
62573 }
62574 
62575 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)
62576 {
62577  ma_result result;
62578 
62579  result = ma_mp3_init_internal(pConfig, pMP3);
62580  if (result != MA_SUCCESS) {
62581  return result;
62582  }
62583 
62584  #if !defined(MA_NO_MP3)
62585  {
62586  ma_bool32 mp3Result;
62587 
62588  mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks);
62589  if (mp3Result != MA_TRUE) {
62590  return MA_INVALID_FILE;
62591  }
62592 
62593  ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
62594 
62595  return MA_SUCCESS;
62596  }
62597  #else
62598  {
62599  /* mp3 is disabled. */
62600  (void)pFilePath;
62601  (void)pAllocationCallbacks;
62602  return MA_NOT_IMPLEMENTED;
62603  }
62604  #endif
62605 }
62606 
62607 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)
62608 {
62609  ma_result result;
62610 
62611  result = ma_mp3_init_internal(pConfig, pMP3);
62612  if (result != MA_SUCCESS) {
62613  return result;
62614  }
62615 
62616  #if !defined(MA_NO_MP3)
62617  {
62618  ma_bool32 mp3Result;
62619 
62620  mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks);
62621  if (mp3Result != MA_TRUE) {
62622  return MA_INVALID_FILE;
62623  }
62624 
62625  ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
62626 
62627  return MA_SUCCESS;
62628  }
62629  #else
62630  {
62631  /* mp3 is disabled. */
62632  (void)pData;
62633  (void)dataSize;
62634  (void)pAllocationCallbacks;
62635  return MA_NOT_IMPLEMENTED;
62636  }
62637  #endif
62638 }
62639 
62640 MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks)
62641 {
62642  if (pMP3 == NULL) {
62643  return;
62644  }
62645 
62646  #if !defined(MA_NO_MP3)
62647  {
62648  ma_dr_mp3_uninit(&pMP3->dr);
62649  }
62650  #else
62651  {
62652  /* mp3 is disabled. Should never hit this since initialization would have failed. */
62653  MA_ASSERT(MA_FALSE);
62654  }
62655  #endif
62656 
62657  /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */
62658  ma_free(pMP3->pSeekPoints, pAllocationCallbacks);
62659 
62660  ma_data_source_uninit(&pMP3->ds);
62661 }
62662 
62663 MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
62664 {
62665  if (pFramesRead != NULL) {
62666  *pFramesRead = 0;
62667  }
62668 
62669  if (frameCount == 0) {
62670  return MA_INVALID_ARGS;
62671  }
62672 
62673  if (pMP3 == NULL) {
62674  return MA_INVALID_ARGS;
62675  }
62676 
62677  #if !defined(MA_NO_MP3)
62678  {
62679  /* We always use floating point format. */
62680  ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
62681  ma_uint64 totalFramesRead = 0;
62682  ma_format format;
62683 
62684  ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0);
62685 
62686  switch (format)
62687  {
62688  case ma_format_f32:
62689  {
62690  totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut);
62691  } break;
62692 
62693  case ma_format_s16:
62694  {
62695  totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut);
62696  } break;
62697 
62698  case ma_format_u8:
62699  case ma_format_s24:
62700  case ma_format_s32:
62701  case ma_format_unknown:
62702  default:
62703  {
62704  return MA_INVALID_OPERATION;
62705  };
62706  }
62707 
62708  /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */
62709  if (totalFramesRead == 0) {
62710  result = MA_AT_END;
62711  }
62712 
62713  if (pFramesRead != NULL) {
62714  *pFramesRead = totalFramesRead;
62715  }
62716 
62717  return result;
62718  }
62719  #else
62720  {
62721  /* mp3 is disabled. Should never hit this since initialization would have failed. */
62722  MA_ASSERT(MA_FALSE);
62723 
62724  (void)pFramesOut;
62725  (void)frameCount;
62726  (void)pFramesRead;
62727 
62728  return MA_NOT_IMPLEMENTED;
62729  }
62730  #endif
62731 }
62732 
62733 MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex)
62734 {
62735  if (pMP3 == NULL) {
62736  return MA_INVALID_ARGS;
62737  }
62738 
62739  #if !defined(MA_NO_MP3)
62740  {
62741  ma_bool32 mp3Result;
62742 
62743  mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex);
62744  if (mp3Result != MA_TRUE) {
62745  return MA_ERROR;
62746  }
62747 
62748  return MA_SUCCESS;
62749  }
62750  #else
62751  {
62752  /* mp3 is disabled. Should never hit this since initialization would have failed. */
62753  MA_ASSERT(MA_FALSE);
62754 
62755  (void)frameIndex;
62756 
62757  return MA_NOT_IMPLEMENTED;
62758  }
62759  #endif
62760 }
62761 
62762 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)
62763 {
62764  /* Defaults for safety. */
62765  if (pFormat != NULL) {
62766  *pFormat = ma_format_unknown;
62767  }
62768  if (pChannels != NULL) {
62769  *pChannels = 0;
62770  }
62771  if (pSampleRate != NULL) {
62772  *pSampleRate = 0;
62773  }
62774  if (pChannelMap != NULL) {
62775  MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
62776  }
62777 
62778  if (pMP3 == NULL) {
62779  return MA_INVALID_OPERATION;
62780  }
62781 
62782  if (pFormat != NULL) {
62783  *pFormat = pMP3->format;
62784  }
62785 
62786  #if !defined(MA_NO_MP3)
62787  {
62788  if (pChannels != NULL) {
62789  *pChannels = pMP3->dr.channels;
62790  }
62791 
62792  if (pSampleRate != NULL) {
62793  *pSampleRate = pMP3->dr.sampleRate;
62794  }
62795 
62796  if (pChannelMap != NULL) {
62797  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels);
62798  }
62799 
62800  return MA_SUCCESS;
62801  }
62802  #else
62803  {
62804  /* mp3 is disabled. Should never hit this since initialization would have failed. */
62805  MA_ASSERT(MA_FALSE);
62806  return MA_NOT_IMPLEMENTED;
62807  }
62808  #endif
62809 }
62810 
62811 MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor)
62812 {
62813  if (pCursor == NULL) {
62814  return MA_INVALID_ARGS;
62815  }
62816 
62817  *pCursor = 0; /* Safety. */
62818 
62819  if (pMP3 == NULL) {
62820  return MA_INVALID_ARGS;
62821  }
62822 
62823  #if !defined(MA_NO_MP3)
62824  {
62825  *pCursor = pMP3->dr.currentPCMFrame;
62826 
62827  return MA_SUCCESS;
62828  }
62829  #else
62830  {
62831  /* mp3 is disabled. Should never hit this since initialization would have failed. */
62832  MA_ASSERT(MA_FALSE);
62833  return MA_NOT_IMPLEMENTED;
62834  }
62835  #endif
62836 }
62837 
62838 MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength)
62839 {
62840  if (pLength == NULL) {
62841  return MA_INVALID_ARGS;
62842  }
62843 
62844  *pLength = 0; /* Safety. */
62845 
62846  if (pMP3 == NULL) {
62847  return MA_INVALID_ARGS;
62848  }
62849 
62850  #if !defined(MA_NO_MP3)
62851  {
62852  *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr);
62853 
62854  return MA_SUCCESS;
62855  }
62856  #else
62857  {
62858  /* mp3 is disabled. Should never hit this since initialization would have failed. */
62859  MA_ASSERT(MA_FALSE);
62860  return MA_NOT_IMPLEMENTED;
62861  }
62862  #endif
62863 }
62864 
62865 
62866 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)
62867 {
62868  ma_result result;
62869  ma_mp3* pMP3;
62870 
62871  (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. */
62872 
62873  /* For now we're just allocating the decoder backend on the heap. */
62874  pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
62875  if (pMP3 == NULL) {
62876  return MA_OUT_OF_MEMORY;
62877  }
62878 
62879  result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3);
62880  if (result != MA_SUCCESS) {
62881  ma_free(pMP3, pAllocationCallbacks);
62882  return result;
62883  }
62884 
62885  *ppBackend = pMP3;
62886 
62887  return MA_SUCCESS;
62888 }
62889 
62890 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)
62891 {
62892  ma_result result;
62893  ma_mp3* pMP3;
62894 
62895  (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. */
62896 
62897  /* For now we're just allocating the decoder backend on the heap. */
62898  pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
62899  if (pMP3 == NULL) {
62900  return MA_OUT_OF_MEMORY;
62901  }
62902 
62903  result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3);
62904  if (result != MA_SUCCESS) {
62905  ma_free(pMP3, pAllocationCallbacks);
62906  return result;
62907  }
62908 
62909  *ppBackend = pMP3;
62910 
62911  return MA_SUCCESS;
62912 }
62913 
62914 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)
62915 {
62916  ma_result result;
62917  ma_mp3* pMP3;
62918 
62919  (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. */
62920 
62921  /* For now we're just allocating the decoder backend on the heap. */
62922  pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
62923  if (pMP3 == NULL) {
62924  return MA_OUT_OF_MEMORY;
62925  }
62926 
62927  result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3);
62928  if (result != MA_SUCCESS) {
62929  ma_free(pMP3, pAllocationCallbacks);
62930  return result;
62931  }
62932 
62933  *ppBackend = pMP3;
62934 
62935  return MA_SUCCESS;
62936 }
62937 
62938 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)
62939 {
62940  ma_result result;
62941  ma_mp3* pMP3;
62942 
62943  (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. */
62944 
62945  /* For now we're just allocating the decoder backend on the heap. */
62946  pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
62947  if (pMP3 == NULL) {
62948  return MA_OUT_OF_MEMORY;
62949  }
62950 
62951  result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3);
62952  if (result != MA_SUCCESS) {
62953  ma_free(pMP3, pAllocationCallbacks);
62954  return result;
62955  }
62956 
62957  *ppBackend = pMP3;
62958 
62959  return MA_SUCCESS;
62960 }
62961 
62962 static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
62963 {
62964  ma_mp3* pMP3 = (ma_mp3*)pBackend;
62965 
62966  (void)pUserData;
62967 
62968  ma_mp3_uninit(pMP3, pAllocationCallbacks);
62969  ma_free(pMP3, pAllocationCallbacks);
62970 }
62971 
62972 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 =
62973 {
62974  ma_decoding_backend_init__mp3,
62975  ma_decoding_backend_init_file__mp3,
62976  ma_decoding_backend_init_file_w__mp3,
62977  ma_decoding_backend_init_memory__mp3,
62978  ma_decoding_backend_uninit__mp3
62979 };
62980 
62981 static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62982 {
62983  return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder);
62984 }
62985 
62986 static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62987 {
62988  return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder);
62989 }
62990 
62991 static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62992 {
62993  return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder);
62994 }
62995 
62996 static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62997 {
62998  return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder);
62999 }
63000 #endif /* ma_dr_mp3_h */
63001 
63002 /* Vorbis */
63003 #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
63004 #define MA_HAS_VORBIS
63005 
63006 /* The size in bytes of each chunk of data to read from the Vorbis stream. */
63007 #define MA_VORBIS_DATA_CHUNK_SIZE 4096
63008 
63009 typedef struct
63010 {
63012  ma_read_proc onRead;
63013  ma_seek_proc onSeek;
63014  ma_tell_proc onTell;
63015  void* pReadSeekTellUserData;
63016  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. */
63017  ma_format format; /* Only f32 is allowed with stb_vorbis. */
63018  ma_uint32 channels;
63019  ma_uint32 sampleRate;
63020  ma_uint64 cursor;
63021 #if !defined(MA_NO_VORBIS)
63022  stb_vorbis* stb;
63023  ma_bool32 usingPushMode;
63024  struct
63025  {
63026  ma_uint8* pData;
63027  size_t dataSize;
63028  size_t dataCapacity;
63029  size_t audioStartOffsetInBytes;
63030  ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
63031  ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
63032  float** ppPacketData;
63033  } push;
63034 #endif
63035 } ma_stbvorbis;
63036 
63037 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);
63038 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);
63039 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);
63040 MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
63041 MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
63042 MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex);
63043 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);
63044 MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor);
63045 MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength);
63046 
63047 
63048 static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63049 {
63050  return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
63051 }
63052 
63053 static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
63054 {
63055  return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex);
63056 }
63057 
63058 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)
63059 {
63060  return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
63061 }
63062 
63063 static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
63064 {
63065  return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor);
63066 }
63067 
63068 static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
63069 {
63070  return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength);
63071 }
63072 
63073 static ma_data_source_vtable g_ma_stbvorbis_ds_vtable =
63074 {
63075  ma_stbvorbis_ds_read,
63076  ma_stbvorbis_ds_seek,
63077  ma_stbvorbis_ds_get_data_format,
63078  ma_stbvorbis_ds_get_cursor,
63079  ma_stbvorbis_ds_get_length,
63080  NULL, /* onSetLooping */
63081  0
63082 };
63083 
63084 
63085 static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis)
63086 {
63087  ma_result result;
63088  ma_data_source_config dataSourceConfig;
63089 
63090  (void)pConfig;
63091 
63092  if (pVorbis == NULL) {
63093  return MA_INVALID_ARGS;
63094  }
63095 
63096  MA_ZERO_OBJECT(pVorbis);
63097  pVorbis->format = ma_format_f32; /* Only supporting f32. */
63098 
63099  dataSourceConfig = ma_data_source_config_init();
63100  dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable;
63101 
63102  result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
63103  if (result != MA_SUCCESS) {
63104  return result; /* Failed to initialize the base data source. */
63105  }
63106 
63107  return MA_SUCCESS;
63108 }
63109 
63110 #if !defined(MA_NO_VORBIS)
63111 static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis)
63112 {
63113  stb_vorbis_info info;
63114 
63115  MA_ASSERT(pVorbis != NULL);
63116 
63117  info = stb_vorbis_get_info(pVorbis->stb);
63118 
63119  pVorbis->channels = info.channels;
63120  pVorbis->sampleRate = info.sample_rate;
63121 
63122  return MA_SUCCESS;
63123 }
63124 
63125 static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis)
63126 {
63127  ma_result result;
63128  stb_vorbis* stb;
63129  size_t dataSize = 0;
63130  size_t dataCapacity = 0;
63131  ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */
63132 
63133  for (;;) {
63134  int vorbisError;
63135  int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */
63136  size_t bytesRead;
63137  ma_uint8* pNewData;
63138 
63139  /* Allocate memory for the new chunk. */
63140  dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
63141  pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks);
63142  if (pNewData == NULL) {
63143  ma_free(pData, &pVorbis->allocationCallbacks);
63144  return MA_OUT_OF_MEMORY;
63145  }
63146 
63147  pData = pNewData;
63148 
63149  /* Read in the next chunk. */
63150  result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead);
63151  dataSize += bytesRead;
63152 
63153  if (result != MA_SUCCESS) {
63154  ma_free(pData, &pVorbis->allocationCallbacks);
63155  return result;
63156  }
63157 
63158  /* We have a maximum of 31 bits with stb_vorbis. */
63159  if (dataSize > INT_MAX) {
63160  ma_free(pData, &pVorbis->allocationCallbacks);
63161  return MA_TOO_BIG;
63162  }
63163 
63164  stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
63165  if (stb != NULL) {
63166  /*
63167  Successfully opened the Vorbis decoder. We might have some leftover unprocessed
63168  data so we'll need to move that down to the front.
63169  */
63170  dataSize -= (size_t)consumedDataSize; /* Consume the data. */
63171  MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize);
63172 
63173  /*
63174  We need to track the start point so we can seek back to the start of the audio
63175  data when seeking.
63176  */
63177  pVorbis->push.audioStartOffsetInBytes = consumedDataSize;
63178 
63179  break;
63180  } else {
63181  /* Failed to open the decoder. */
63182  if (vorbisError == VORBIS_need_more_data) {
63183  continue;
63184  } else {
63185  ma_free(pData, &pVorbis->allocationCallbacks);
63186  return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
63187  }
63188  }
63189  }
63190 
63191  MA_ASSERT(stb != NULL);
63192  pVorbis->stb = stb;
63193  pVorbis->push.pData = pData;
63194  pVorbis->push.dataSize = dataSize;
63195  pVorbis->push.dataCapacity = dataCapacity;
63196 
63197  return MA_SUCCESS;
63198 }
63199 #endif
63200 
63201 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)
63202 {
63203  ma_result result;
63204 
63205  result = ma_stbvorbis_init_internal(pConfig, pVorbis);
63206  if (result != MA_SUCCESS) {
63207  return result;
63208  }
63209 
63210  if (onRead == NULL || onSeek == NULL) {
63211  return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
63212  }
63213 
63214  pVorbis->onRead = onRead;
63215  pVorbis->onSeek = onSeek;
63216  pVorbis->onTell = onTell;
63217  pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
63218  ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks);
63219 
63220  #if !defined(MA_NO_VORBIS)
63221  {
63222  /*
63223  stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the
63224  pushing API. In order for us to be able to successfully initialize the decoder we need to
63225  supply it with enough data. We need to keep loading data until we have enough.
63226  */
63227  result = ma_stbvorbis_init_internal_decoder_push(pVorbis);
63228  if (result != MA_SUCCESS) {
63229  return result;
63230  }
63231 
63232  pVorbis->usingPushMode = MA_TRUE;
63233 
63234  result = ma_stbvorbis_post_init(pVorbis);
63235  if (result != MA_SUCCESS) {
63236  stb_vorbis_close(pVorbis->stb);
63237  ma_free(pVorbis->push.pData, pAllocationCallbacks);
63238  return result;
63239  }
63240 
63241  return MA_SUCCESS;
63242  }
63243  #else
63244  {
63245  /* vorbis is disabled. */
63246  (void)pAllocationCallbacks;
63247  return MA_NOT_IMPLEMENTED;
63248  }
63249  #endif
63250 }
63251 
63252 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)
63253 {
63254  ma_result result;
63255 
63256  result = ma_stbvorbis_init_internal(pConfig, pVorbis);
63257  if (result != MA_SUCCESS) {
63258  return result;
63259  }
63260 
63261  #if !defined(MA_NO_VORBIS)
63262  {
63263  (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */
63264 
63265  /* We can use stb_vorbis' pull mode for file based streams. */
63266  pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL);
63267  if (pVorbis->stb == NULL) {
63268  return MA_INVALID_FILE;
63269  }
63270 
63271  pVorbis->usingPushMode = MA_FALSE;
63272 
63273  result = ma_stbvorbis_post_init(pVorbis);
63274  if (result != MA_SUCCESS) {
63275  stb_vorbis_close(pVorbis->stb);
63276  return result;
63277  }
63278 
63279  return MA_SUCCESS;
63280  }
63281  #else
63282  {
63283  /* vorbis is disabled. */
63284  (void)pFilePath;
63285  (void)pAllocationCallbacks;
63286  return MA_NOT_IMPLEMENTED;
63287  }
63288  #endif
63289 }
63290 
63291 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)
63292 {
63293  ma_result result;
63294 
63295  result = ma_stbvorbis_init_internal(pConfig, pVorbis);
63296  if (result != MA_SUCCESS) {
63297  return result;
63298  }
63299 
63300  #if !defined(MA_NO_VORBIS)
63301  {
63302  (void)pAllocationCallbacks;
63303 
63304  /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */
63305  if (dataSize > INT_MAX) {
63306  return MA_TOO_BIG;
63307  }
63308 
63309  pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
63310  if (pVorbis->stb == NULL) {
63311  return MA_INVALID_FILE;
63312  }
63313 
63314  pVorbis->usingPushMode = MA_FALSE;
63315 
63316  result = ma_stbvorbis_post_init(pVorbis);
63317  if (result != MA_SUCCESS) {
63318  stb_vorbis_close(pVorbis->stb);
63319  return result;
63320  }
63321 
63322  return MA_SUCCESS;
63323  }
63324  #else
63325  {
63326  /* vorbis is disabled. */
63327  (void)pData;
63328  (void)dataSize;
63329  (void)pAllocationCallbacks;
63330  return MA_NOT_IMPLEMENTED;
63331  }
63332  #endif
63333 }
63334 
63335 MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
63336 {
63337  if (pVorbis == NULL) {
63338  return;
63339  }
63340 
63341  #if !defined(MA_NO_VORBIS)
63342  {
63343  stb_vorbis_close(pVorbis->stb);
63344 
63345  /* We'll have to clear some memory if we're using push mode. */
63346  if (pVorbis->usingPushMode) {
63347  ma_free(pVorbis->push.pData, pAllocationCallbacks);
63348  }
63349  }
63350  #else
63351  {
63352  /* vorbis is disabled. Should never hit this since initialization would have failed. */
63353  MA_ASSERT(MA_FALSE);
63354  }
63355  #endif
63356 
63357  ma_data_source_uninit(&pVorbis->ds);
63358 }
63359 
63360 MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63361 {
63362  if (pFramesRead != NULL) {
63363  *pFramesRead = 0;
63364  }
63365 
63366  if (frameCount == 0) {
63367  return MA_INVALID_ARGS;
63368  }
63369 
63370  if (pVorbis == NULL) {
63371  return MA_INVALID_ARGS;
63372  }
63373 
63374  #if !defined(MA_NO_VORBIS)
63375  {
63376  /* We always use floating point format. */
63377  ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
63378  ma_uint64 totalFramesRead = 0;
63379  ma_format format;
63380  ma_uint32 channels;
63381 
63382  ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
63383 
63384  if (format == ma_format_f32) {
63385  /* We read differently depending on whether or not we're using push mode. */
63386  if (pVorbis->usingPushMode) {
63387  /* Push mode. This is the complex case. */
63388  float* pFramesOutF32 = (float*)pFramesOut;
63389 
63390  while (totalFramesRead < frameCount) {
63391  /* The first thing to do is read from any already-cached frames. */
63392  ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */
63393 
63394  /* The output pointer can be null in which case we just treate it as a seek. */
63395  if (pFramesOut != NULL) {
63396  ma_uint64 iFrame;
63397  for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
63398  ma_uint32 iChannel;
63399  for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) {
63400  pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame];
63401  }
63402 
63403  pFramesOutF32 += pVorbis->channels;
63404  }
63405  }
63406 
63407  /* Update pointers and counters. */
63408  pVorbis->push.framesConsumed += framesToReadFromCache;
63409  pVorbis->push.framesRemaining -= framesToReadFromCache;
63410  totalFramesRead += framesToReadFromCache;
63411 
63412  /* Don't bother reading any more frames right now if we've just finished loading. */
63413  if (totalFramesRead == frameCount) {
63414  break;
63415  }
63416 
63417  MA_ASSERT(pVorbis->push.framesRemaining == 0);
63418 
63419  /* Getting here means we've run out of cached frames. We'll need to load some more. */
63420  for (;;) {
63421  int samplesRead = 0;
63422  int consumedDataSize;
63423 
63424  /* We need to case dataSize to an int, so make sure we can do it safely. */
63425  if (pVorbis->push.dataSize > INT_MAX) {
63426  break; /* Too big. */
63427  }
63428 
63429  consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead);
63430  if (consumedDataSize != 0) {
63431  /* Successfully decoded a Vorbis frame. Consume the data. */
63432  pVorbis->push.dataSize -= (size_t)consumedDataSize;
63433  MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize);
63434 
63435  pVorbis->push.framesConsumed = 0;
63436  pVorbis->push.framesRemaining = samplesRead;
63437 
63438  break;
63439  } else {
63440  /* Not enough data. Read more. */
63441  size_t bytesRead;
63442 
63443  /* Expand the data buffer if necessary. */
63444  if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) {
63445  size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
63446  ma_uint8* pNewData;
63447 
63448  pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks);
63449  if (pNewData == NULL) {
63450  result = MA_OUT_OF_MEMORY;
63451  break;
63452  }
63453 
63454  pVorbis->push.pData = pNewData;
63455  pVorbis->push.dataCapacity = newCap;
63456  }
63457 
63458  /* We should have enough room to load some data. */
63459  result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead);
63460  pVorbis->push.dataSize += bytesRead;
63461 
63462  if (result != MA_SUCCESS) {
63463  break; /* Failed to read any data. Get out. */
63464  }
63465  }
63466  }
63467 
63468  /* 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). */
63469  if (result != MA_SUCCESS) {
63470  break;
63471  }
63472  }
63473  } else {
63474  /* 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. */
63475  while (totalFramesRead < frameCount) {
63476  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
63477  int framesRead;
63478 
63479  if (framesRemaining > INT_MAX) {
63480  framesRemaining = INT_MAX;
63481  }
63482 
63483  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. */
63484  totalFramesRead += framesRead;
63485 
63486  if (framesRead < (int)framesRemaining) {
63487  break; /* Nothing left to read. Get out. */
63488  }
63489  }
63490  }
63491  } else {
63492  result = MA_INVALID_ARGS;
63493  }
63494 
63495  pVorbis->cursor += totalFramesRead;
63496 
63497  if (totalFramesRead == 0) {
63498  result = MA_AT_END;
63499  }
63500 
63501  if (pFramesRead != NULL) {
63502  *pFramesRead = totalFramesRead;
63503  }
63504 
63505  if (result == MA_SUCCESS && totalFramesRead == 0) {
63506  result = MA_AT_END;
63507  }
63508 
63509  return result;
63510  }
63511  #else
63512  {
63513  /* vorbis is disabled. Should never hit this since initialization would have failed. */
63514  MA_ASSERT(MA_FALSE);
63515 
63516  (void)pFramesOut;
63517  (void)frameCount;
63518  (void)pFramesRead;
63519 
63520  return MA_NOT_IMPLEMENTED;
63521  }
63522  #endif
63523 }
63524 
63525 MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex)
63526 {
63527  if (pVorbis == NULL) {
63528  return MA_INVALID_ARGS;
63529  }
63530 
63531  #if !defined(MA_NO_VORBIS)
63532  {
63533  /* Different seeking methods depending on whether or not we're using push mode. */
63534  if (pVorbis->usingPushMode) {
63535  /* Push mode. This is the complex case. */
63536  ma_result result;
63537  float buffer[4096];
63538 
63539  /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */
63540  if (frameIndex < pVorbis->cursor) {
63541  if (frameIndex > 0x7FFFFFFF) {
63542  return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
63543  }
63544 
63545  /*
63546  This is wildly inefficient due to me having trouble getting sample exact seeking working
63547  robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work
63548  perfectly is to reinitialize the decoder. Note that we only enter this path when seeking
63549  backwards. This will hopefully be removed once we get our own Vorbis decoder implemented.
63550  */
63551  stb_vorbis_close(pVorbis->stb);
63552  ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks);
63553 
63554  MA_ZERO_OBJECT(&pVorbis->push);
63555 
63556  /* Seek to the start of the file. */
63557  result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start);
63558  if (result != MA_SUCCESS) {
63559  return result;
63560  }
63561 
63562  result = ma_stbvorbis_init_internal_decoder_push(pVorbis);
63563  if (result != MA_SUCCESS) {
63564  return result;
63565  }
63566 
63567  /* At this point we should be sitting on the first frame. */
63568  pVorbis->cursor = 0;
63569  }
63570 
63571  /* We're just brute-forcing this for now. */
63572  while (pVorbis->cursor < frameIndex) {
63573  ma_uint64 framesRead;
63574  ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels;
63575  if (framesToRead > (frameIndex - pVorbis->cursor)) {
63576  framesToRead = (frameIndex - pVorbis->cursor);
63577  }
63578 
63579  result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead);
63580  if (result != MA_SUCCESS) {
63581  return result;
63582  }
63583  }
63584  } else {
63585  /* Pull mode. This is the simple case. */
63586  int vorbisResult;
63587 
63588  if (frameIndex > UINT_MAX) {
63589  return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
63590  }
63591 
63592  vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */
63593  if (vorbisResult == 0) {
63594  return MA_ERROR; /* See failed. */
63595  }
63596 
63597  pVorbis->cursor = frameIndex;
63598  }
63599 
63600  return MA_SUCCESS;
63601  }
63602  #else
63603  {
63604  /* vorbis is disabled. Should never hit this since initialization would have failed. */
63605  MA_ASSERT(MA_FALSE);
63606 
63607  (void)frameIndex;
63608 
63609  return MA_NOT_IMPLEMENTED;
63610  }
63611  #endif
63612 }
63613 
63614 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)
63615 {
63616  /* Defaults for safety. */
63617  if (pFormat != NULL) {
63618  *pFormat = ma_format_unknown;
63619  }
63620  if (pChannels != NULL) {
63621  *pChannels = 0;
63622  }
63623  if (pSampleRate != NULL) {
63624  *pSampleRate = 0;
63625  }
63626  if (pChannelMap != NULL) {
63627  MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
63628  }
63629 
63630  if (pVorbis == NULL) {
63631  return MA_INVALID_OPERATION;
63632  }
63633 
63634  if (pFormat != NULL) {
63635  *pFormat = pVorbis->format;
63636  }
63637 
63638  #if !defined(MA_NO_VORBIS)
63639  {
63640  if (pChannels != NULL) {
63641  *pChannels = pVorbis->channels;
63642  }
63643 
63644  if (pSampleRate != NULL) {
63645  *pSampleRate = pVorbis->sampleRate;
63646  }
63647 
63648  if (pChannelMap != NULL) {
63649  ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels);
63650  }
63651 
63652  return MA_SUCCESS;
63653  }
63654  #else
63655  {
63656  /* vorbis is disabled. Should never hit this since initialization would have failed. */
63657  MA_ASSERT(MA_FALSE);
63658  return MA_NOT_IMPLEMENTED;
63659  }
63660  #endif
63661 }
63662 
63663 MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor)
63664 {
63665  if (pCursor == NULL) {
63666  return MA_INVALID_ARGS;
63667  }
63668 
63669  *pCursor = 0; /* Safety. */
63670 
63671  if (pVorbis == NULL) {
63672  return MA_INVALID_ARGS;
63673  }
63674 
63675  #if !defined(MA_NO_VORBIS)
63676  {
63677  *pCursor = pVorbis->cursor;
63678 
63679  return MA_SUCCESS;
63680  }
63681  #else
63682  {
63683  /* vorbis is disabled. Should never hit this since initialization would have failed. */
63684  MA_ASSERT(MA_FALSE);
63685  return MA_NOT_IMPLEMENTED;
63686  }
63687  #endif
63688 }
63689 
63690 MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength)
63691 {
63692  if (pLength == NULL) {
63693  return MA_INVALID_ARGS;
63694  }
63695 
63696  *pLength = 0; /* Safety. */
63697 
63698  if (pVorbis == NULL) {
63699  return MA_INVALID_ARGS;
63700  }
63701 
63702  #if !defined(MA_NO_VORBIS)
63703  {
63704  if (pVorbis->usingPushMode) {
63705  *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */
63706  } else {
63707  *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb);
63708  }
63709 
63710  return MA_SUCCESS;
63711  }
63712  #else
63713  {
63714  /* vorbis is disabled. Should never hit this since initialization would have failed. */
63715  MA_ASSERT(MA_FALSE);
63716  return MA_NOT_IMPLEMENTED;
63717  }
63718  #endif
63719 }
63720 
63721 
63722 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)
63723 {
63724  ma_result result;
63725  ma_stbvorbis* pVorbis;
63726 
63727  (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. */
63728 
63729  /* For now we're just allocating the decoder backend on the heap. */
63730  pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
63731  if (pVorbis == NULL) {
63732  return MA_OUT_OF_MEMORY;
63733  }
63734 
63735  result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
63736  if (result != MA_SUCCESS) {
63737  ma_free(pVorbis, pAllocationCallbacks);
63738  return result;
63739  }
63740 
63741  *ppBackend = pVorbis;
63742 
63743  return MA_SUCCESS;
63744 }
63745 
63746 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)
63747 {
63748  ma_result result;
63749  ma_stbvorbis* pVorbis;
63750 
63751  (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. */
63752 
63753  /* For now we're just allocating the decoder backend on the heap. */
63754  pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
63755  if (pVorbis == NULL) {
63756  return MA_OUT_OF_MEMORY;
63757  }
63758 
63759  result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
63760  if (result != MA_SUCCESS) {
63761  ma_free(pVorbis, pAllocationCallbacks);
63762  return result;
63763  }
63764 
63765  *ppBackend = pVorbis;
63766 
63767  return MA_SUCCESS;
63768 }
63769 
63770 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)
63771 {
63772  ma_result result;
63773  ma_stbvorbis* pVorbis;
63774 
63775  (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. */
63776 
63777  /* For now we're just allocating the decoder backend on the heap. */
63778  pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
63779  if (pVorbis == NULL) {
63780  return MA_OUT_OF_MEMORY;
63781  }
63782 
63783  result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis);
63784  if (result != MA_SUCCESS) {
63785  ma_free(pVorbis, pAllocationCallbacks);
63786  return result;
63787  }
63788 
63789  *ppBackend = pVorbis;
63790 
63791  return MA_SUCCESS;
63792 }
63793 
63794 static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
63795 {
63796  ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
63797 
63798  (void)pUserData;
63799 
63800  ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks);
63801  ma_free(pVorbis, pAllocationCallbacks);
63802 }
63803 
63804 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis =
63805 {
63806  ma_decoding_backend_init__stbvorbis,
63807  ma_decoding_backend_init_file__stbvorbis,
63808  NULL, /* onInitFileW() */
63809  ma_decoding_backend_init_memory__stbvorbis,
63810  ma_decoding_backend_uninit__stbvorbis
63811 };
63812 
63813 static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63814 {
63815  return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder);
63816 }
63817 
63818 static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63819 {
63820  return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder);
63821 }
63822 
63823 static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63824 {
63825  return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder);
63826 }
63827 
63828 static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63829 {
63830  return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder);
63831 }
63832 #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
63833 
63834 
63835 
63836 static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63837 {
63838  MA_ASSERT(pDecoder != NULL);
63839 
63840  if (pConfig != NULL) {
63841  return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
63842  } else {
63843  pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
63844  return MA_SUCCESS;
63845  }
63846 }
63847 
63848 static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63849 {
63850  return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead);
63851 }
63852 
63853 static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
63854 {
63855  return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);
63856 }
63857 
63858 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)
63859 {
63860  return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
63861 }
63862 
63863 static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
63864 {
63865  return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor);
63866 }
63867 
63868 static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
63869 {
63870  return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength);
63871 }
63872 
63873 static ma_data_source_vtable g_ma_decoder_data_source_vtable =
63874 {
63875  ma_decoder__data_source_on_read,
63876  ma_decoder__data_source_on_seek,
63877  ma_decoder__data_source_on_get_data_format,
63878  ma_decoder__data_source_on_get_cursor,
63879  ma_decoder__data_source_on_get_length,
63880  NULL, /* onSetLooping */
63881  0
63882 };
63883 
63884 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)
63885 {
63886  ma_result result;
63887  ma_data_source_config dataSourceConfig;
63888 
63889  MA_ASSERT(pConfig != NULL);
63890 
63891  if (pDecoder == NULL) {
63892  return MA_INVALID_ARGS;
63893  }
63894 
63895  MA_ZERO_OBJECT(pDecoder);
63896 
63897  dataSourceConfig = ma_data_source_config_init();
63898  dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable;
63899 
63900  result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds);
63901  if (result != MA_SUCCESS) {
63902  return result;
63903  }
63904 
63905  pDecoder->onRead = onRead;
63906  pDecoder->onSeek = onSeek;
63907  pDecoder->onTell = onTell;
63908  pDecoder->pUserData = pUserData;
63909 
63910  result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
63911  if (result != MA_SUCCESS) {
63912  ma_data_source_uninit(&pDecoder->ds);
63913  return result;
63914  }
63915 
63916  return MA_SUCCESS;
63917 }
63918 
63919 static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63920 {
63921  ma_result result;
63922 
63923  result = ma_decoder__init_data_converter(pDecoder, pConfig);
63924 
63925  /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */
63926  if (result != MA_SUCCESS) {
63927  ma_decoder_uninit(pDecoder);
63928  return result;
63929  }
63930 
63931  return result;
63932 }
63933 
63934 
63935 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)
63936 {
63937  ma_result result = MA_NO_BACKEND;
63938 
63939  MA_ASSERT(pConfig != NULL);
63940  MA_ASSERT(pDecoder != NULL);
63941 
63942  /* Silence some warnings in the case that we don't have any decoder backends enabled. */
63943  (void)onRead;
63944  (void)onSeek;
63945  (void)pUserData;
63946 
63947 
63948  /* If we've specified a specific encoding type, try that first. */
63949  if (pConfig->encodingFormat != ma_encoding_format_unknown) {
63950  #ifdef MA_HAS_WAV
63951  if (pConfig->encodingFormat == ma_encoding_format_wav) {
63952  result = ma_decoder_init_wav__internal(pConfig, pDecoder);
63953  }
63954  #endif
63955  #ifdef MA_HAS_FLAC
63956  if (pConfig->encodingFormat == ma_encoding_format_flac) {
63957  result = ma_decoder_init_flac__internal(pConfig, pDecoder);
63958  }
63959  #endif
63960  #ifdef MA_HAS_MP3
63961  if (pConfig->encodingFormat == ma_encoding_format_mp3) {
63962  result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
63963  }
63964  #endif
63965  #ifdef MA_HAS_VORBIS
63966  if (pConfig->encodingFormat == ma_encoding_format_vorbis) {
63967  result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
63968  }
63969  #endif
63970 
63971  /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */
63972  if (result != MA_SUCCESS) {
63973  onSeek(pDecoder, 0, ma_seek_origin_start);
63974  }
63975  }
63976 
63977  if (result != MA_SUCCESS) {
63978  /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */
63979 
63980  /*
63981  We use trial and error to open a decoder. We prioritize custom decoders so that if they
63982  implement the same encoding format they take priority over the built-in decoders.
63983  */
63984  if (result != MA_SUCCESS) {
63985  result = ma_decoder_init_custom__internal(pConfig, pDecoder);
63986  if (result != MA_SUCCESS) {
63987  onSeek(pDecoder, 0, ma_seek_origin_start);
63988  }
63989  }
63990 
63991  /*
63992  If we get to this point and we still haven't found a decoder, and the caller has requested a
63993  specific encoding format, there's no hope for it. Abort.
63994  */
63995  if (pConfig->encodingFormat != ma_encoding_format_unknown) {
63996  return MA_NO_BACKEND;
63997  }
63998 
63999  #ifdef MA_HAS_WAV
64000  if (result != MA_SUCCESS) {
64001  result = ma_decoder_init_wav__internal(pConfig, pDecoder);
64002  if (result != MA_SUCCESS) {
64003  onSeek(pDecoder, 0, ma_seek_origin_start);
64004  }
64005  }
64006  #endif
64007  #ifdef MA_HAS_FLAC
64008  if (result != MA_SUCCESS) {
64009  result = ma_decoder_init_flac__internal(pConfig, pDecoder);
64010  if (result != MA_SUCCESS) {
64011  onSeek(pDecoder, 0, ma_seek_origin_start);
64012  }
64013  }
64014  #endif
64015  #ifdef MA_HAS_MP3
64016  if (result != MA_SUCCESS) {
64017  result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
64018  if (result != MA_SUCCESS) {
64019  onSeek(pDecoder, 0, ma_seek_origin_start);
64020  }
64021  }
64022  #endif
64023  #ifdef MA_HAS_VORBIS
64024  if (result != MA_SUCCESS) {
64025  result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
64026  if (result != MA_SUCCESS) {
64027  onSeek(pDecoder, 0, ma_seek_origin_start);
64028  }
64029  }
64030  #endif
64031  }
64032 
64033  if (result != MA_SUCCESS) {
64034  return result;
64035  }
64036 
64037  return ma_decoder__postinit(pConfig, pDecoder);
64038 }
64039 
64040 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)
64041 {
64042  ma_decoder_config config;
64043  ma_result result;
64044 
64045  config = ma_decoder_config_init_copy(pConfig);
64046 
64047  result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
64048  if (result != MA_SUCCESS) {
64049  return result;
64050  }
64051 
64052  return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
64053 }
64054 
64055 
64056 static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
64057 {
64058  size_t bytesRemaining;
64059 
64060  MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos);
64061 
64062  if (pBytesRead != NULL) {
64063  *pBytesRead = 0;
64064  }
64065 
64066  bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos;
64067  if (bytesToRead > bytesRemaining) {
64068  bytesToRead = bytesRemaining;
64069  }
64070 
64071  if (bytesRemaining == 0) {
64072  return MA_AT_END;
64073  }
64074 
64075  if (bytesToRead > 0) {
64076  MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead);
64077  pDecoder->data.memory.currentReadPos += bytesToRead;
64078  }
64079 
64080  if (pBytesRead != NULL) {
64081  *pBytesRead = bytesToRead;
64082  }
64083 
64084  return MA_SUCCESS;
64085 }
64086 
64087 static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
64088 {
64089  if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) {
64090  return MA_BAD_SEEK;
64091  }
64092 
64093  if (origin == ma_seek_origin_current) {
64094  if (byteOffset > 0) {
64095  if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) {
64096  byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */
64097  }
64098 
64099  pDecoder->data.memory.currentReadPos += (size_t)byteOffset;
64100  } else {
64101  if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) {
64102  byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */
64103  }
64104 
64105  pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset;
64106  }
64107  } else {
64108  if (origin == ma_seek_origin_end) {
64109  if (byteOffset < 0) {
64110  byteOffset = -byteOffset;
64111  }
64112 
64113  if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) {
64114  pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */
64115  } else {
64116  pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset;
64117  }
64118  } else {
64119  if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) {
64120  pDecoder->data.memory.currentReadPos = (size_t)byteOffset;
64121  } else {
64122  pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */
64123  }
64124  }
64125  }
64126 
64127  return MA_SUCCESS;
64128 }
64129 
64130 static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor)
64131 {
64132  MA_ASSERT(pDecoder != NULL);
64133  MA_ASSERT(pCursor != NULL);
64134 
64135  *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos;
64136 
64137  return MA_SUCCESS;
64138 }
64139 
64140 static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64141 {
64142  ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder);
64143  if (result != MA_SUCCESS) {
64144  return result;
64145  }
64146 
64147  if (pData == NULL || dataSize == 0) {
64148  return MA_INVALID_ARGS;
64149  }
64150 
64151  pDecoder->data.memory.pData = (const ma_uint8*)pData;
64152  pDecoder->data.memory.dataSize = dataSize;
64153  pDecoder->data.memory.currentReadPos = 0;
64154 
64155  (void)pConfig;
64156  return MA_SUCCESS;
64157 }
64158 
64159 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64160 {
64161  ma_result result;
64162  ma_decoder_config config;
64163 
64164  config = ma_decoder_config_init_copy(pConfig);
64165 
64166  result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder);
64167  if (result != MA_SUCCESS) {
64168  return result;
64169  }
64170 
64171  if (pData == NULL || dataSize == 0) {
64172  return MA_INVALID_ARGS;
64173  }
64174 
64175  /* 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. */
64176  result = MA_NO_BACKEND;
64177 
64178  if (config.encodingFormat != ma_encoding_format_unknown) {
64179  #ifdef MA_HAS_WAV
64180  if (config.encodingFormat == ma_encoding_format_wav) {
64181  result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder);
64182  }
64183  #endif
64184  #ifdef MA_HAS_FLAC
64185  if (config.encodingFormat == ma_encoding_format_flac) {
64186  result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder);
64187  }
64188  #endif
64189  #ifdef MA_HAS_MP3
64190  if (config.encodingFormat == ma_encoding_format_mp3) {
64191  result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder);
64192  }
64193  #endif
64194  #ifdef MA_HAS_VORBIS
64195  if (config.encodingFormat == ma_encoding_format_vorbis) {
64196  result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder);
64197  }
64198  #endif
64199  }
64200 
64201  if (result != MA_SUCCESS) {
64202  /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
64203 
64204  /*
64205  We use trial and error to open a decoder. We prioritize custom decoders so that if they
64206  implement the same encoding format they take priority over the built-in decoders.
64207  */
64208  result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder);
64209 
64210  /*
64211  If we get to this point and we still haven't found a decoder, and the caller has requested a
64212  specific encoding format, there's no hope for it. Abort.
64213  */
64214  if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
64215  return MA_NO_BACKEND;
64216  }
64217 
64218  /* Use trial and error for stock decoders. */
64219  if (result != MA_SUCCESS) {
64220  #ifdef MA_HAS_WAV
64221  if (result != MA_SUCCESS) {
64222  result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder);
64223  }
64224  #endif
64225  #ifdef MA_HAS_FLAC
64226  if (result != MA_SUCCESS) {
64227  result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder);
64228  }
64229  #endif
64230  #ifdef MA_HAS_MP3
64231  if (result != MA_SUCCESS) {
64232  result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder);
64233  }
64234  #endif
64235  #ifdef MA_HAS_VORBIS
64236  if (result != MA_SUCCESS) {
64237  result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder);
64238  }
64239  #endif
64240  }
64241  }
64242 
64243  /*
64244  If at this point we still haven't successfully initialized the decoder it most likely means
64245  the backend doesn't have an implementation for loading from a file path. We'll try using
64246  miniaudio's built-in file IO for loading file.
64247  */
64248  if (result == MA_SUCCESS) {
64249  /* Initialization was successful. Finish up. */
64250  result = ma_decoder__postinit(&config, pDecoder);
64251  if (result != MA_SUCCESS) {
64252  /*
64253  The backend was initialized successfully, but for some reason post-initialization failed. This is most likely
64254  due to an out of memory error. We're going to abort with an error here and not try to recover.
64255  */
64256  if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
64257  pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);
64258  }
64259 
64260  return result;
64261  }
64262  } else {
64263  /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */
64264  result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder);
64265  if (result != MA_SUCCESS) {
64266  return result;
64267  }
64268 
64269  result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
64270  if (result != MA_SUCCESS) {
64271  return result;
64272  }
64273  }
64274 
64275  return MA_SUCCESS;
64276 }
64277 
64278 
64279 #if defined(MA_HAS_WAV) || \
64280  defined(MA_HAS_MP3) || \
64281  defined(MA_HAS_FLAC) || \
64282  defined(MA_HAS_VORBIS) || \
64283  defined(MA_HAS_OPUS)
64284 #define MA_HAS_PATH_API
64285 #endif
64286 
64287 #if defined(MA_HAS_PATH_API)
64288 static const char* ma_path_file_name(const char* path)
64289 {
64290  const char* fileName;
64291 
64292  if (path == NULL) {
64293  return NULL;
64294  }
64295 
64296  fileName = path;
64297 
64298  /* We just loop through the path until we find the last slash. */
64299  while (path[0] != '\0') {
64300  if (path[0] == '/' || path[0] == '\\') {
64301  fileName = path;
64302  }
64303 
64304  path += 1;
64305  }
64306 
64307  /* At this point the file name is sitting on a slash, so just move forward. */
64308  while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
64309  fileName += 1;
64310  }
64311 
64312  return fileName;
64313 }
64314 
64315 static const wchar_t* ma_path_file_name_w(const wchar_t* path)
64316 {
64317  const wchar_t* fileName;
64318 
64319  if (path == NULL) {
64320  return NULL;
64321  }
64322 
64323  fileName = path;
64324 
64325  /* We just loop through the path until we find the last slash. */
64326  while (path[0] != '\0') {
64327  if (path[0] == '/' || path[0] == '\\') {
64328  fileName = path;
64329  }
64330 
64331  path += 1;
64332  }
64333 
64334  /* At this point the file name is sitting on a slash, so just move forward. */
64335  while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
64336  fileName += 1;
64337  }
64338 
64339  return fileName;
64340 }
64341 
64342 
64343 static const char* ma_path_extension(const char* path)
64344 {
64345  const char* extension;
64346  const char* lastOccurance;
64347 
64348  if (path == NULL) {
64349  path = "";
64350  }
64351 
64352  extension = ma_path_file_name(path);
64353  lastOccurance = NULL;
64354 
64355  /* Just find the last '.' and return. */
64356  while (extension[0] != '\0') {
64357  if (extension[0] == '.') {
64358  extension += 1;
64359  lastOccurance = extension;
64360  }
64361 
64362  extension += 1;
64363  }
64364 
64365  return (lastOccurance != NULL) ? lastOccurance : extension;
64366 }
64367 
64368 static const wchar_t* ma_path_extension_w(const wchar_t* path)
64369 {
64370  const wchar_t* extension;
64371  const wchar_t* lastOccurance;
64372 
64373  if (path == NULL) {
64374  path = L"";
64375  }
64376 
64377  extension = ma_path_file_name_w(path);
64378  lastOccurance = NULL;
64379 
64380  /* Just find the last '.' and return. */
64381  while (extension[0] != '\0') {
64382  if (extension[0] == '.') {
64383  extension += 1;
64384  lastOccurance = extension;
64385  }
64386 
64387  extension += 1;
64388  }
64389 
64390  return (lastOccurance != NULL) ? lastOccurance : extension;
64391 }
64392 
64393 
64394 static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
64395 {
64396  const char* ext1;
64397  const char* ext2;
64398 
64399  if (path == NULL || extension == NULL) {
64400  return MA_FALSE;
64401  }
64402 
64403  ext1 = extension;
64404  ext2 = ma_path_extension(path);
64405 
64406 #if defined(_MSC_VER) || defined(__DMC__)
64407  return _stricmp(ext1, ext2) == 0;
64408 #else
64409  return strcasecmp(ext1, ext2) == 0;
64410 #endif
64411 }
64412 
64413 static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
64414 {
64415  const wchar_t* ext1;
64416  const wchar_t* ext2;
64417 
64418  if (path == NULL || extension == NULL) {
64419  return MA_FALSE;
64420  }
64421 
64422  ext1 = extension;
64423  ext2 = ma_path_extension_w(path);
64424 
64425 #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)
64426  return _wcsicmp(ext1, ext2) == 0;
64427 #else
64428  /*
64429  I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
64430  isn't the most efficient way to do it, but it should work OK.
64431  */
64432  {
64433  char ext1MB[4096];
64434  char ext2MB[4096];
64435  const wchar_t* pext1 = ext1;
64436  const wchar_t* pext2 = ext2;
64437  mbstate_t mbs1;
64438  mbstate_t mbs2;
64439 
64440  MA_ZERO_OBJECT(&mbs1);
64441  MA_ZERO_OBJECT(&mbs2);
64442 
64443  if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
64444  return MA_FALSE;
64445  }
64446  if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
64447  return MA_FALSE;
64448  }
64449 
64450  return strcasecmp(ext1MB, ext2MB) == 0;
64451  }
64452 #endif
64453 }
64454 #endif /* MA_HAS_PATH_API */
64455 
64456 
64457 
64458 static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
64459 {
64460  MA_ASSERT(pDecoder != NULL);
64461  MA_ASSERT(pBufferOut != NULL);
64462 
64463  return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead);
64464 }
64465 
64466 static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin)
64467 {
64468  MA_ASSERT(pDecoder != NULL);
64469 
64470  return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin);
64471 }
64472 
64473 static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor)
64474 {
64475  MA_ASSERT(pDecoder != NULL);
64476 
64477  return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor);
64478 }
64479 
64480 static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64481 {
64482  ma_result result;
64483  ma_vfs_file file;
64484 
64485  result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
64486  if (result != MA_SUCCESS) {
64487  return result;
64488  }
64489 
64490  if (pFilePath == NULL || pFilePath[0] == '\0') {
64491  return MA_INVALID_ARGS;
64492  }
64493 
64494  result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
64495  if (result != MA_SUCCESS) {
64496  return result;
64497  }
64498 
64499  pDecoder->data.vfs.pVFS = pVFS;
64500  pDecoder->data.vfs.file = file;
64501 
64502  return MA_SUCCESS;
64503 }
64504 
64505 MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64506 {
64507  ma_result result;
64508  ma_decoder_config config;
64509 
64510  config = ma_decoder_config_init_copy(pConfig);
64511  result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
64512  if (result != MA_SUCCESS) {
64513  return result;
64514  }
64515 
64516  result = MA_NO_BACKEND;
64517 
64518  if (config.encodingFormat != ma_encoding_format_unknown) {
64519  #ifdef MA_HAS_WAV
64520  if (config.encodingFormat == ma_encoding_format_wav) {
64521  result = ma_decoder_init_wav__internal(&config, pDecoder);
64522  }
64523  #endif
64524  #ifdef MA_HAS_FLAC
64525  if (config.encodingFormat == ma_encoding_format_flac) {
64526  result = ma_decoder_init_flac__internal(&config, pDecoder);
64527  }
64528  #endif
64529  #ifdef MA_HAS_MP3
64530  if (config.encodingFormat == ma_encoding_format_mp3) {
64531  result = ma_decoder_init_mp3__internal(&config, pDecoder);
64532  }
64533  #endif
64534  #ifdef MA_HAS_VORBIS
64535  if (config.encodingFormat == ma_encoding_format_vorbis) {
64536  result = ma_decoder_init_vorbis__internal(&config, pDecoder);
64537  }
64538  #endif
64539 
64540  /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
64541  if (result != MA_SUCCESS) {
64542  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64543  }
64544  }
64545 
64546  if (result != MA_SUCCESS) {
64547  /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
64548 
64549  /*
64550  We use trial and error to open a decoder. We prioritize custom decoders so that if they
64551  implement the same encoding format they take priority over the built-in decoders.
64552  */
64553  if (result != MA_SUCCESS) {
64554  result = ma_decoder_init_custom__internal(&config, pDecoder);
64555  if (result != MA_SUCCESS) {
64556  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64557  }
64558  }
64559 
64560  /*
64561  If we get to this point and we still haven't found a decoder, and the caller has requested a
64562  specific encoding format, there's no hope for it. Abort.
64563  */
64564  if (config.encodingFormat != ma_encoding_format_unknown) {
64565  return MA_NO_BACKEND;
64566  }
64567 
64568  #ifdef MA_HAS_WAV
64569  if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
64570  result = ma_decoder_init_wav__internal(&config, pDecoder);
64571  if (result != MA_SUCCESS) {
64572  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64573  }
64574  }
64575  #endif
64576  #ifdef MA_HAS_FLAC
64577  if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
64578  result = ma_decoder_init_flac__internal(&config, pDecoder);
64579  if (result != MA_SUCCESS) {
64580  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64581  }
64582  }
64583  #endif
64584  #ifdef MA_HAS_MP3
64585  if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
64586  result = ma_decoder_init_mp3__internal(&config, pDecoder);
64587  if (result != MA_SUCCESS) {
64588  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64589  }
64590  }
64591  #endif
64592  }
64593 
64594  /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
64595  if (result != MA_SUCCESS) {
64596  result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
64597  } else {
64598  result = ma_decoder__postinit(&config, pDecoder);
64599  }
64600 
64601  if (result != MA_SUCCESS) {
64602  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. */
64603  ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
64604  }
64605 
64606  return result;
64607  }
64608 
64609  return MA_SUCCESS;
64610 }
64611 
64612 
64613 static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64614 {
64615  ma_result result;
64616  ma_vfs_file file;
64617 
64618  result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
64619  if (result != MA_SUCCESS) {
64620  return result;
64621  }
64622 
64623  if (pFilePath == NULL || pFilePath[0] == '\0') {
64624  return MA_INVALID_ARGS;
64625  }
64626 
64627  result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
64628  if (result != MA_SUCCESS) {
64629  return result;
64630  }
64631 
64632  pDecoder->data.vfs.pVFS = pVFS;
64633  pDecoder->data.vfs.file = file;
64634 
64635  return MA_SUCCESS;
64636 }
64637 
64638 MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64639 {
64640  ma_result result;
64641  ma_decoder_config config;
64642 
64643  config = ma_decoder_config_init_copy(pConfig);
64644  result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
64645  if (result != MA_SUCCESS) {
64646  return result;
64647  }
64648 
64649  result = MA_NO_BACKEND;
64650 
64651  if (config.encodingFormat != ma_encoding_format_unknown) {
64652  #ifdef MA_HAS_WAV
64653  if (config.encodingFormat == ma_encoding_format_wav) {
64654  result = ma_decoder_init_wav__internal(&config, pDecoder);
64655  }
64656  #endif
64657  #ifdef MA_HAS_FLAC
64658  if (config.encodingFormat == ma_encoding_format_flac) {
64659  result = ma_decoder_init_flac__internal(&config, pDecoder);
64660  }
64661  #endif
64662  #ifdef MA_HAS_MP3
64663  if (config.encodingFormat == ma_encoding_format_mp3) {
64664  result = ma_decoder_init_mp3__internal(&config, pDecoder);
64665  }
64666  #endif
64667  #ifdef MA_HAS_VORBIS
64668  if (config.encodingFormat == ma_encoding_format_vorbis) {
64669  result = ma_decoder_init_vorbis__internal(&config, pDecoder);
64670  }
64671  #endif
64672 
64673  /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
64674  if (result != MA_SUCCESS) {
64675  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64676  }
64677  }
64678 
64679  if (result != MA_SUCCESS) {
64680  /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
64681 
64682  /*
64683  We use trial and error to open a decoder. We prioritize custom decoders so that if they
64684  implement the same encoding format they take priority over the built-in decoders.
64685  */
64686  if (result != MA_SUCCESS) {
64687  result = ma_decoder_init_custom__internal(&config, pDecoder);
64688  if (result != MA_SUCCESS) {
64689  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64690  }
64691  }
64692 
64693  /*
64694  If we get to this point and we still haven't found a decoder, and the caller has requested a
64695  specific encoding format, there's no hope for it. Abort.
64696  */
64697  if (config.encodingFormat != ma_encoding_format_unknown) {
64698  return MA_NO_BACKEND;
64699  }
64700 
64701  #ifdef MA_HAS_WAV
64702  if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
64703  result = ma_decoder_init_wav__internal(&config, pDecoder);
64704  if (result != MA_SUCCESS) {
64705  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64706  }
64707  }
64708  #endif
64709  #ifdef MA_HAS_FLAC
64710  if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
64711  result = ma_decoder_init_flac__internal(&config, pDecoder);
64712  if (result != MA_SUCCESS) {
64713  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64714  }
64715  }
64716  #endif
64717  #ifdef MA_HAS_MP3
64718  if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
64719  result = ma_decoder_init_mp3__internal(&config, pDecoder);
64720  if (result != MA_SUCCESS) {
64721  ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64722  }
64723  }
64724  #endif
64725  }
64726 
64727  /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
64728  if (result != MA_SUCCESS) {
64729  result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
64730  } else {
64731  result = ma_decoder__postinit(&config, pDecoder);
64732  }
64733 
64734  if (result != MA_SUCCESS) {
64735  ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
64736  return result;
64737  }
64738 
64739  return MA_SUCCESS;
64740 }
64741 
64742 
64743 static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64744 {
64745  ma_result result;
64746 
64747  result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder);
64748  if (result != MA_SUCCESS) {
64749  return result;
64750  }
64751 
64752  if (pFilePath == NULL || pFilePath[0] == '\0') {
64753  return MA_INVALID_ARGS;
64754  }
64755 
64756  return MA_SUCCESS;
64757 }
64758 
64759 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64760 {
64761  ma_result result;
64762  ma_decoder_config config;
64763 
64764  config = ma_decoder_config_init_copy(pConfig);
64765  result = ma_decoder__preinit_file(pFilePath, &config, pDecoder);
64766  if (result != MA_SUCCESS) {
64767  return result;
64768  }
64769 
64770  /* 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. */
64771  result = MA_NO_BACKEND;
64772 
64773  if (config.encodingFormat != ma_encoding_format_unknown) {
64774  #ifdef MA_HAS_WAV
64775  if (config.encodingFormat == ma_encoding_format_wav) {
64776  result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
64777  }
64778  #endif
64779  #ifdef MA_HAS_FLAC
64780  if (config.encodingFormat == ma_encoding_format_flac) {
64781  result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
64782  }
64783  #endif
64784  #ifdef MA_HAS_MP3
64785  if (config.encodingFormat == ma_encoding_format_mp3) {
64786  result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
64787  }
64788  #endif
64789  #ifdef MA_HAS_VORBIS
64790  if (config.encodingFormat == ma_encoding_format_vorbis) {
64791  result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
64792  }
64793  #endif
64794  }
64795 
64796  if (result != MA_SUCCESS) {
64797  /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
64798 
64799  /*
64800  We use trial and error to open a decoder. We prioritize custom decoders so that if they
64801  implement the same encoding format they take priority over the built-in decoders.
64802  */
64803  result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder);
64804 
64805  /*
64806  If we get to this point and we still haven't found a decoder, and the caller has requested a
64807  specific encoding format, there's no hope for it. Abort.
64808  */
64809  if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
64810  return MA_NO_BACKEND;
64811  }
64812 
64813  /* First try loading based on the file extension so we don't waste time opening and closing files. */
64814  #ifdef MA_HAS_WAV
64815  if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
64816  result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
64817  }
64818  #endif
64819  #ifdef MA_HAS_FLAC
64820  if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
64821  result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
64822  }
64823  #endif
64824  #ifdef MA_HAS_MP3
64825  if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
64826  result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
64827  }
64828  #endif
64829  #ifdef MA_HAS_VORBIS
64830  if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) {
64831  result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
64832  }
64833  #endif
64834 
64835  /*
64836  If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we
64837  need only iterate over our stock decoders.
64838  */
64839  if (result != MA_SUCCESS) {
64840  #ifdef MA_HAS_WAV
64841  if (result != MA_SUCCESS) {
64842  result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
64843  }
64844  #endif
64845  #ifdef MA_HAS_FLAC
64846  if (result != MA_SUCCESS) {
64847  result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
64848  }
64849  #endif
64850  #ifdef MA_HAS_MP3
64851  if (result != MA_SUCCESS) {
64852  result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
64853  }
64854  #endif
64855  #ifdef MA_HAS_VORBIS
64856  if (result != MA_SUCCESS) {
64857  result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
64858  }
64859  #endif
64860  }
64861  }
64862 
64863  /*
64864  If at this point we still haven't successfully initialized the decoder it most likely means
64865  the backend doesn't have an implementation for loading from a file path. We'll try using
64866  miniaudio's built-in file IO for loading file.
64867  */
64868  if (result == MA_SUCCESS) {
64869  /* Initialization was successful. Finish up. */
64870  result = ma_decoder__postinit(&config, pDecoder);
64871  if (result != MA_SUCCESS) {
64872  /*
64873  The backend was initialized successfully, but for some reason post-initialization failed. This is most likely
64874  due to an out of memory error. We're going to abort with an error here and not try to recover.
64875  */
64876  if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
64877  pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);
64878  }
64879 
64880  return result;
64881  }
64882  } else {
64883  /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */
64884  result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder);
64885  if (result != MA_SUCCESS) {
64886  return result;
64887  }
64888  }
64889 
64890  return MA_SUCCESS;
64891 }
64892 
64893 static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64894 {
64895  ma_result result;
64896 
64897  result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder);
64898  if (result != MA_SUCCESS) {
64899  return result;
64900  }
64901 
64902  if (pFilePath == NULL || pFilePath[0] == '\0') {
64903  return MA_INVALID_ARGS;
64904  }
64905 
64906  return MA_SUCCESS;
64907 }
64908 
64909 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64910 {
64911  ma_result result;
64912  ma_decoder_config config;
64913 
64914  config = ma_decoder_config_init_copy(pConfig);
64915  result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder);
64916  if (result != MA_SUCCESS) {
64917  return result;
64918  }
64919 
64920  /* 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. */
64921  result = MA_NO_BACKEND;
64922 
64923  if (config.encodingFormat != ma_encoding_format_unknown) {
64924  #ifdef MA_HAS_WAV
64925  if (config.encodingFormat == ma_encoding_format_wav) {
64926  result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
64927  }
64928  #endif
64929  #ifdef MA_HAS_FLAC
64930  if (config.encodingFormat == ma_encoding_format_flac) {
64931  result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
64932  }
64933  #endif
64934  #ifdef MA_HAS_MP3
64935  if (config.encodingFormat == ma_encoding_format_mp3) {
64936  result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
64937  }
64938  #endif
64939  #ifdef MA_HAS_VORBIS
64940  if (config.encodingFormat == ma_encoding_format_vorbis) {
64941  result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
64942  }
64943  #endif
64944  }
64945 
64946  if (result != MA_SUCCESS) {
64947  /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
64948 
64949  /*
64950  We use trial and error to open a decoder. We prioritize custom decoders so that if they
64951  implement the same encoding format they take priority over the built-in decoders.
64952  */
64953  result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder);
64954 
64955  /*
64956  If we get to this point and we still haven't found a decoder, and the caller has requested a
64957  specific encoding format, there's no hope for it. Abort.
64958  */
64959  if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
64960  return MA_NO_BACKEND;
64961  }
64962 
64963  /* First try loading based on the file extension so we don't waste time opening and closing files. */
64964  #ifdef MA_HAS_WAV
64965  if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
64966  result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
64967  }
64968  #endif
64969  #ifdef MA_HAS_FLAC
64970  if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
64971  result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
64972  }
64973  #endif
64974  #ifdef MA_HAS_MP3
64975  if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
64976  result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
64977  }
64978  #endif
64979  #ifdef MA_HAS_VORBIS
64980  if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) {
64981  result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
64982  }
64983  #endif
64984 
64985  /*
64986  If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we
64987  need only iterate over our stock decoders.
64988  */
64989  if (result != MA_SUCCESS) {
64990  #ifdef MA_HAS_WAV
64991  if (result != MA_SUCCESS) {
64992  result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
64993  }
64994  #endif
64995  #ifdef MA_HAS_FLAC
64996  if (result != MA_SUCCESS) {
64997  result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
64998  }
64999  #endif
65000  #ifdef MA_HAS_MP3
65001  if (result != MA_SUCCESS) {
65002  result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
65003  }
65004  #endif
65005  #ifdef MA_HAS_VORBIS
65006  if (result != MA_SUCCESS) {
65007  result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
65008  }
65009  #endif
65010  }
65011  }
65012 
65013  /*
65014  If at this point we still haven't successfully initialized the decoder it most likely means
65015  the backend doesn't have an implementation for loading from a file path. We'll try using
65016  miniaudio's built-in file IO for loading file.
65017  */
65018  if (result == MA_SUCCESS) {
65019  /* Initialization was successful. Finish up. */
65020  result = ma_decoder__postinit(&config, pDecoder);
65021  if (result != MA_SUCCESS) {
65022  /*
65023  The backend was initialized successfully, but for some reason post-initialization failed. This is most likely
65024  due to an out of memory error. We're going to abort with an error here and not try to recover.
65025  */
65026  if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
65027  pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);
65028  }
65029 
65030  return result;
65031  }
65032  } else {
65033  /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */
65034  result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);
65035  if (result != MA_SUCCESS) {
65036  return result;
65037  }
65038  }
65039 
65040  return MA_SUCCESS;
65041 }
65042 
65043 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)
65044 {
65045  if (pDecoder == NULL) {
65046  return MA_INVALID_ARGS;
65047  }
65048 
65049  if (pDecoder->pBackend != NULL) {
65050  if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
65051  pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);
65052  }
65053  }
65054 
65055  if (pDecoder->onRead == ma_decoder__on_read_vfs) {
65056  ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);
65057  pDecoder->data.vfs.file = NULL;
65058  }
65059 
65060  ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
65061  ma_data_source_uninit(&pDecoder->ds);
65062 
65063  if (pDecoder->pInputCache != NULL) {
65064  ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks);
65065  }
65066 
65067  return MA_SUCCESS;
65068 }
65069 
65070 MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65071 {
65072  ma_result result = MA_SUCCESS;
65073  ma_uint64 totalFramesReadOut;
65074  void* pRunningFramesOut;
65075 
65076  if (pFramesRead != NULL) {
65077  *pFramesRead = 0; /* Safety. */
65078  }
65079 
65080  if (frameCount == 0) {
65081  return MA_INVALID_ARGS;
65082  }
65083 
65084  if (pDecoder == NULL) {
65085  return MA_INVALID_ARGS;
65086  }
65087 
65088  if (pDecoder->pBackend == NULL) {
65089  return MA_INVALID_OPERATION;
65090  }
65091 
65092  /* Fast path. */
65093  if (pDecoder->converter.isPassthrough) {
65094  result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut);
65095  } else {
65096  /*
65097  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
65098  need to run through each sample because we need to ensure it's internal cache is updated.
65099  */
65100  if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
65101  result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut);
65102  } else {
65103  /* Slow path. Need to run everything through the data converter. */
65104  ma_format internalFormat;
65105  ma_uint32 internalChannels;
65106 
65107  totalFramesReadOut = 0;
65108  pRunningFramesOut = pFramesOut;
65109 
65110  result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0);
65111  if (result != MA_SUCCESS) {
65112  return result; /* Failed to retrieve the internal format and channel count. */
65113  }
65114 
65115  /*
65116  We run a different path depending on whether or not we are using a heap-allocated
65117  intermediary buffer or not. If the data converter does not support the calculation of
65118  the required number of input frames, we'll use the heap-allocated path. Otherwise we'll
65119  use the stack-allocated path.
65120  */
65121  if (pDecoder->pInputCache != NULL) {
65122  /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */
65123  while (totalFramesReadOut < frameCount) {
65124  ma_uint64 framesToReadThisIterationIn;
65125  ma_uint64 framesToReadThisIterationOut;
65126 
65127  /* If there's any data available in the cache, that needs to get processed first. */
65128  if (pDecoder->inputCacheRemaining > 0) {
65129  framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
65130  framesToReadThisIterationIn = framesToReadThisIterationOut;
65131  if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) {
65132  framesToReadThisIterationIn = pDecoder->inputCacheRemaining;
65133  }
65134 
65135  result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut);
65136  if (result != MA_SUCCESS) {
65137  break;
65138  }
65139 
65140  pDecoder->inputCacheConsumed += framesToReadThisIterationIn;
65141  pDecoder->inputCacheRemaining -= framesToReadThisIterationIn;
65142 
65143  totalFramesReadOut += framesToReadThisIterationOut;
65144 
65145  if (pRunningFramesOut != NULL) {
65146  pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
65147  }
65148 
65149  if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
65150  break; /* We're done. */
65151  }
65152  }
65153 
65154  /* Getting here means there's no data in the cache and we need to fill it up from the data source. */
65155  if (pDecoder->inputCacheRemaining == 0) {
65156  pDecoder->inputCacheConsumed = 0;
65157 
65158  result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining);
65159  if (result != MA_SUCCESS) {
65160  break;
65161  }
65162  }
65163  }
65164  } else {
65165  /* We have a way of determining the required number of input frames so just use the stack. */
65166  while (totalFramesReadOut < frameCount) {
65167  ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
65168  ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels);
65169  ma_uint64 framesToReadThisIterationIn;
65170  ma_uint64 framesReadThisIterationIn;
65171  ma_uint64 framesToReadThisIterationOut;
65172  ma_uint64 framesReadThisIterationOut;
65173  ma_uint64 requiredInputFrameCount;
65174 
65175  framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
65176  framesToReadThisIterationIn = framesToReadThisIterationOut;
65177  if (framesToReadThisIterationIn > intermediaryBufferCap) {
65178  framesToReadThisIterationIn = intermediaryBufferCap;
65179  }
65180 
65181  ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount);
65182  if (framesToReadThisIterationIn > requiredInputFrameCount) {
65183  framesToReadThisIterationIn = requiredInputFrameCount;
65184  }
65185 
65186  if (requiredInputFrameCount > 0) {
65187  result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn);
65188  } else {
65189  framesReadThisIterationIn = 0;
65190  }
65191 
65192  /*
65193  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
65194  input frames, we still want to try processing frames because there may some output frames generated from cached input data.
65195  */
65196  framesReadThisIterationOut = framesToReadThisIterationOut;
65197  result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
65198  if (result != MA_SUCCESS) {
65199  break;
65200  }
65201 
65202  totalFramesReadOut += framesReadThisIterationOut;
65203 
65204  if (pRunningFramesOut != NULL) {
65205  pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
65206  }
65207 
65208  if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
65209  break; /* We're done. */
65210  }
65211  }
65212  }
65213  }
65214  }
65215 
65216  pDecoder->readPointerInPCMFrames += totalFramesReadOut;
65217 
65218  if (pFramesRead != NULL) {
65219  *pFramesRead = totalFramesReadOut;
65220  }
65221 
65222  if (result == MA_SUCCESS && totalFramesReadOut == 0) {
65223  result = MA_AT_END;
65224  }
65225 
65226  return result;
65227 }
65228 
65229 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex)
65230 {
65231  if (pDecoder == NULL) {
65232  return MA_INVALID_ARGS;
65233  }
65234 
65235  if (pDecoder->pBackend != NULL) {
65236  ma_result result;
65237  ma_uint64 internalFrameIndex;
65238  ma_uint32 internalSampleRate;
65239  ma_uint64 currentFrameIndex;
65240 
65241  result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
65242  if (result != MA_SUCCESS) {
65243  return result; /* Failed to retrieve the internal sample rate. */
65244  }
65245 
65246  if (internalSampleRate == pDecoder->outputSampleRate) {
65247  internalFrameIndex = frameIndex;
65248  } else {
65249  internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex);
65250  }
65251 
65252  /* Only seek if we're requesting a different frame to what we're currently sitting on. */
65253  ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, &currentFrameIndex);
65254  if (currentFrameIndex != internalFrameIndex) {
65255  result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
65256  if (result == MA_SUCCESS) {
65257  pDecoder->readPointerInPCMFrames = frameIndex;
65258  }
65259 
65260  /* Reset the data converter so that any cached data in the resampler is cleared. */
65261  ma_data_converter_reset(&pDecoder->converter);
65262  }
65263 
65264  return result;
65265  }
65266 
65267  /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
65268  return MA_INVALID_ARGS;
65269 }
65270 
65271 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)
65272 {
65273  if (pDecoder == NULL) {
65274  return MA_INVALID_ARGS;
65275  }
65276 
65277  if (pFormat != NULL) {
65278  *pFormat = pDecoder->outputFormat;
65279  }
65280 
65281  if (pChannels != NULL) {
65282  *pChannels = pDecoder->outputChannels;
65283  }
65284 
65285  if (pSampleRate != NULL) {
65286  *pSampleRate = pDecoder->outputSampleRate;
65287  }
65288 
65289  if (pChannelMap != NULL) {
65290  ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap);
65291  }
65292 
65293  return MA_SUCCESS;
65294 }
65295 
65296 MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor)
65297 {
65298  if (pCursor == NULL) {
65299  return MA_INVALID_ARGS;
65300  }
65301 
65302  *pCursor = 0;
65303 
65304  if (pDecoder == NULL) {
65305  return MA_INVALID_ARGS;
65306  }
65307 
65308  *pCursor = pDecoder->readPointerInPCMFrames;
65309 
65310  return MA_SUCCESS;
65311 }
65312 
65313 MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength)
65314 {
65315  if (pLength == NULL) {
65316  return MA_INVALID_ARGS;
65317  }
65318 
65319  *pLength = 0;
65320 
65321  if (pDecoder == NULL) {
65322  return MA_INVALID_ARGS;
65323  }
65324 
65325  if (pDecoder->pBackend != NULL) {
65326  ma_result result;
65327  ma_uint64 internalLengthInPCMFrames;
65328  ma_uint32 internalSampleRate;
65329 
65330  result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames);
65331  if (result != MA_SUCCESS) {
65332  return result; /* Failed to retrieve the internal length. */
65333  }
65334 
65335  result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
65336  if (result != MA_SUCCESS) {
65337  return result; /* Failed to retrieve the internal sample rate. */
65338  }
65339 
65340  if (internalSampleRate == pDecoder->outputSampleRate) {
65341  *pLength = internalLengthInPCMFrames;
65342  } else {
65343  *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames);
65344  }
65345 
65346  return MA_SUCCESS;
65347  } else {
65348  return MA_NO_BACKEND;
65349  }
65350 }
65351 
65352 MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames)
65353 {
65354  ma_result result;
65355  ma_uint64 totalFrameCount;
65356 
65357  if (pAvailableFrames == NULL) {
65358  return MA_INVALID_ARGS;
65359  }
65360 
65361  *pAvailableFrames = 0;
65362 
65363  if (pDecoder == NULL) {
65364  return MA_INVALID_ARGS;
65365  }
65366 
65367  result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
65368  if (result != MA_SUCCESS) {
65369  return result;
65370  }
65371 
65372  if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
65373  *pAvailableFrames = 0;
65374  } else {
65375  *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
65376  }
65377 
65378  return MA_SUCCESS;
65379 }
65380 
65381 
65382 static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
65383 {
65384  ma_result result;
65385  ma_uint64 totalFrameCount;
65386  ma_uint64 bpf;
65387  ma_uint64 dataCapInFrames;
65388  void* pPCMFramesOut;
65389 
65390  MA_ASSERT(pDecoder != NULL);
65391 
65392  totalFrameCount = 0;
65393  bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
65394 
65395  /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
65396  dataCapInFrames = 0;
65397  pPCMFramesOut = NULL;
65398  for (;;) {
65399  ma_uint64 frameCountToTryReading;
65400  ma_uint64 framesJustRead;
65401 
65402  /* Make room if there's not enough. */
65403  if (totalFrameCount == dataCapInFrames) {
65404  void* pNewPCMFramesOut;
65405  ma_uint64 newDataCapInFrames = dataCapInFrames*2;
65406  if (newDataCapInFrames == 0) {
65407  newDataCapInFrames = 4096;
65408  }
65409 
65410  if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
65411  ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
65412  return MA_TOO_BIG;
65413  }
65414 
65415  pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
65416  if (pNewPCMFramesOut == NULL) {
65417  ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
65418  return MA_OUT_OF_MEMORY;
65419  }
65420 
65421  dataCapInFrames = newDataCapInFrames;
65422  pPCMFramesOut = pNewPCMFramesOut;
65423  }
65424 
65425  frameCountToTryReading = dataCapInFrames - totalFrameCount;
65426  MA_ASSERT(frameCountToTryReading > 0);
65427 
65428  result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead);
65429  totalFrameCount += framesJustRead;
65430 
65431  if (result != MA_SUCCESS) {
65432  break;
65433  }
65434 
65435  if (framesJustRead < frameCountToTryReading) {
65436  break;
65437  }
65438  }
65439 
65440 
65441  if (pConfigOut != NULL) {
65442  pConfigOut->format = pDecoder->outputFormat;
65443  pConfigOut->channels = pDecoder->outputChannels;
65444  pConfigOut->sampleRate = pDecoder->outputSampleRate;
65445  }
65446 
65447  if (ppPCMFramesOut != NULL) {
65448  *ppPCMFramesOut = pPCMFramesOut;
65449  } else {
65450  ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
65451  }
65452 
65453  if (pFrameCountOut != NULL) {
65454  *pFrameCountOut = totalFrameCount;
65455  }
65456 
65457  ma_decoder_uninit(pDecoder);
65458  return MA_SUCCESS;
65459 }
65460 
65461 MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
65462 {
65463  ma_result result;
65464  ma_decoder_config config;
65465  ma_decoder decoder;
65466 
65467  if (pFrameCountOut != NULL) {
65468  *pFrameCountOut = 0;
65469  }
65470  if (ppPCMFramesOut != NULL) {
65471  *ppPCMFramesOut = NULL;
65472  }
65473 
65474  config = ma_decoder_config_init_copy(pConfig);
65475 
65476  result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder);
65477  if (result != MA_SUCCESS) {
65478  return result;
65479  }
65480 
65481  result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
65482 
65483  return result;
65484 }
65485 
65486 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
65487 {
65488  return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut);
65489 }
65490 
65491 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
65492 {
65493  ma_decoder_config config;
65494  ma_decoder decoder;
65495  ma_result result;
65496 
65497  if (pFrameCountOut != NULL) {
65498  *pFrameCountOut = 0;
65499  }
65500  if (ppPCMFramesOut != NULL) {
65501  *ppPCMFramesOut = NULL;
65502  }
65503 
65504  if (pData == NULL || dataSize == 0) {
65505  return MA_INVALID_ARGS;
65506  }
65507 
65508  config = ma_decoder_config_init_copy(pConfig);
65509 
65510  result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
65511  if (result != MA_SUCCESS) {
65512  return result;
65513  }
65514 
65515  return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
65516 }
65517 #endif /* MA_NO_DECODING */
65518 
65519 
65520 #ifndef MA_NO_ENCODING
65521 
65522 #if defined(MA_HAS_WAV)
65523 static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)
65524 {
65525  ma_encoder* pEncoder = (ma_encoder*)pUserData;
65526  size_t bytesWritten = 0;
65527 
65528  MA_ASSERT(pEncoder != NULL);
65529 
65530  pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten);
65531  return bytesWritten;
65532 }
65533 
65534 static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
65535 {
65536  ma_encoder* pEncoder = (ma_encoder*)pUserData;
65537  ma_result result;
65538 
65539  MA_ASSERT(pEncoder != NULL);
65540 
65541  result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
65542  if (result != MA_SUCCESS) {
65543  return MA_FALSE;
65544  } else {
65545  return MA_TRUE;
65546  }
65547 }
65548 
65549 static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)
65550 {
65551  ma_dr_wav_data_format wavFormat;
65552  ma_allocation_callbacks allocationCallbacks;
65553  ma_dr_wav* pWav;
65554 
65555  MA_ASSERT(pEncoder != NULL);
65556 
65557  pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks);
65558  if (pWav == NULL) {
65559  return MA_OUT_OF_MEMORY;
65560  }
65561 
65562  wavFormat.container = ma_dr_wav_container_riff;
65563  wavFormat.channels = pEncoder->config.channels;
65564  wavFormat.sampleRate = pEncoder->config.sampleRate;
65565  wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;
65566  if (pEncoder->config.format == ma_format_f32) {
65567  wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT;
65568  } else {
65569  wavFormat.format = MA_DR_WAVE_FORMAT_PCM;
65570  }
65571 
65572  allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;
65573  allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc;
65574  allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;
65575  allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree;
65576 
65577  if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {
65578  return MA_ERROR;
65579  }
65580 
65581  pEncoder->pInternalEncoder = pWav;
65582 
65583  return MA_SUCCESS;
65584 }
65585 
65586 static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)
65587 {
65588  ma_dr_wav* pWav;
65589 
65590  MA_ASSERT(pEncoder != NULL);
65591 
65592  pWav = (ma_dr_wav*)pEncoder->pInternalEncoder;
65593  MA_ASSERT(pWav != NULL);
65594 
65595  ma_dr_wav_uninit(pWav);
65596  ma_free(pWav, &pEncoder->config.allocationCallbacks);
65597 }
65598 
65599 static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
65600 {
65601  ma_dr_wav* pWav;
65602  ma_uint64 framesWritten;
65603 
65604  MA_ASSERT(pEncoder != NULL);
65605 
65606  pWav = (ma_dr_wav*)pEncoder->pInternalEncoder;
65607  MA_ASSERT(pWav != NULL);
65608 
65609  framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn);
65610 
65611  if (pFramesWritten != NULL) {
65612  *pFramesWritten = framesWritten;
65613  }
65614 
65615  return MA_SUCCESS;
65616 }
65617 #endif
65618 
65619 MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
65620 {
65621  ma_encoder_config config;
65622 
65623  MA_ZERO_OBJECT(&config);
65624  config.encodingFormat = encodingFormat;
65625  config.format = format;
65626  config.channels = channels;
65627  config.sampleRate = sampleRate;
65628 
65629  return config;
65630 }
65631 
65632 MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65633 {
65634  ma_result result;
65635 
65636  if (pEncoder == NULL) {
65637  return MA_INVALID_ARGS;
65638  }
65639 
65640  MA_ZERO_OBJECT(pEncoder);
65641 
65642  if (pConfig == NULL) {
65643  return MA_INVALID_ARGS;
65644  }
65645 
65646  if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {
65647  return MA_INVALID_ARGS;
65648  }
65649 
65650  pEncoder->config = *pConfig;
65651 
65652  result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);
65653  if (result != MA_SUCCESS) {
65654  return result;
65655  }
65656 
65657  return MA_SUCCESS;
65658 }
65659 
65660 MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)
65661 {
65662  ma_result result = MA_SUCCESS;
65663 
65664  /* This assumes ma_encoder_preinit() has been called prior. */
65665  MA_ASSERT(pEncoder != NULL);
65666 
65667  if (onWrite == NULL || onSeek == NULL) {
65668  return MA_INVALID_ARGS;
65669  }
65670 
65671  pEncoder->onWrite = onWrite;
65672  pEncoder->onSeek = onSeek;
65673  pEncoder->pUserData = pUserData;
65674 
65675  switch (pEncoder->config.encodingFormat)
65676  {
65677  case ma_encoding_format_wav:
65678  {
65679  #if defined(MA_HAS_WAV)
65680  pEncoder->onInit = ma_encoder__on_init_wav;
65681  pEncoder->onUninit = ma_encoder__on_uninit_wav;
65682  pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;
65683  #else
65684  result = MA_NO_BACKEND;
65685  #endif
65686  } break;
65687 
65688  default:
65689  {
65690  result = MA_INVALID_ARGS;
65691  } break;
65692  }
65693 
65694  /* Getting here means we should have our backend callbacks set up. */
65695  if (result == MA_SUCCESS) {
65696  result = pEncoder->onInit(pEncoder);
65697  }
65698 
65699  return result;
65700 }
65701 
65702 static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten)
65703 {
65704  return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten);
65705 }
65706 
65707 static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin)
65708 {
65709  return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin);
65710 }
65711 
65712 MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65713 {
65714  ma_result result;
65715  ma_vfs_file file;
65716 
65717  result = ma_encoder_preinit(pConfig, pEncoder);
65718  if (result != MA_SUCCESS) {
65719  return result;
65720  }
65721 
65722  /* Now open the file. If this fails we don't need to uninitialize the encoder. */
65723  result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
65724  if (result != MA_SUCCESS) {
65725  return result;
65726  }
65727 
65728  pEncoder->data.vfs.pVFS = pVFS;
65729  pEncoder->data.vfs.file = file;
65730 
65731  result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
65732  if (result != MA_SUCCESS) {
65733  ma_vfs_or_default_close(pVFS, file);
65734  return result;
65735  }
65736 
65737  return MA_SUCCESS;
65738 }
65739 
65740 MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65741 {
65742  ma_result result;
65743  ma_vfs_file file;
65744 
65745  result = ma_encoder_preinit(pConfig, pEncoder);
65746  if (result != MA_SUCCESS) {
65747  return result;
65748  }
65749 
65750  /* Now open the file. If this fails we don't need to uninitialize the encoder. */
65751  result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
65752  if (result != MA_SUCCESS) {
65753  return result;
65754  }
65755 
65756  pEncoder->data.vfs.pVFS = pVFS;
65757  pEncoder->data.vfs.file = file;
65758 
65759  result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
65760  if (result != MA_SUCCESS) {
65761  ma_vfs_or_default_close(pVFS, file);
65762  return result;
65763  }
65764 
65765  return MA_SUCCESS;
65766 }
65767 
65768 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65769 {
65770  return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder);
65771 }
65772 
65773 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65774 {
65775  return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder);
65776 }
65777 
65778 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)
65779 {
65780  ma_result result;
65781 
65782  result = ma_encoder_preinit(pConfig, pEncoder);
65783  if (result != MA_SUCCESS) {
65784  return result;
65785  }
65786 
65787  return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);
65788 }
65789 
65790 
65791 MA_API void ma_encoder_uninit(ma_encoder* pEncoder)
65792 {
65793  if (pEncoder == NULL) {
65794  return;
65795  }
65796 
65797  if (pEncoder->onUninit) {
65798  pEncoder->onUninit(pEncoder);
65799  }
65800 
65801  /* If we have a file handle, close it. */
65802  if (pEncoder->onWrite == ma_encoder__on_write_vfs) {
65803  ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file);
65804  pEncoder->data.vfs.file = NULL;
65805  }
65806 }
65807 
65808 
65809 MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
65810 {
65811  if (pFramesWritten != NULL) {
65812  *pFramesWritten = 0;
65813  }
65814 
65815  if (pEncoder == NULL || pFramesIn == NULL) {
65816  return MA_INVALID_ARGS;
65817  }
65818 
65819  return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten);
65820 }
65821 #endif /* MA_NO_ENCODING */
65822 
65823 
65824 
65825 /**************************************************************************************************************************************************************
65826 
65827 Generation
65828 
65829 **************************************************************************************************************************************************************/
65830 #ifndef MA_NO_GENERATION
65831 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)
65832 {
65833  ma_waveform_config config;
65834 
65835  MA_ZERO_OBJECT(&config);
65836  config.format = format;
65837  config.channels = channels;
65838  config.sampleRate = sampleRate;
65839  config.type = type;
65840  config.amplitude = amplitude;
65841  config.frequency = frequency;
65842 
65843  return config;
65844 }
65845 
65846 static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65847 {
65848  return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead);
65849 }
65850 
65851 static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
65852 {
65853  return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);
65854 }
65855 
65856 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)
65857 {
65858  ma_waveform* pWaveform = (ma_waveform*)pDataSource;
65859 
65860  *pFormat = pWaveform->config.format;
65861  *pChannels = pWaveform->config.channels;
65862  *pSampleRate = pWaveform->config.sampleRate;
65863  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels);
65864 
65865  return MA_SUCCESS;
65866 }
65867 
65868 static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
65869 {
65870  ma_waveform* pWaveform = (ma_waveform*)pDataSource;
65871 
65872  *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);
65873 
65874  return MA_SUCCESS;
65875 }
65876 
65877 static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency)
65878 {
65879  return (1.0 / (sampleRate / frequency));
65880 }
65881 
65882 static void ma_waveform__update_advance(ma_waveform* pWaveform)
65883 {
65884  pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
65885 }
65886 
65887 static ma_data_source_vtable g_ma_waveform_data_source_vtable =
65888 {
65889  ma_waveform__data_source_on_read,
65890  ma_waveform__data_source_on_seek,
65891  ma_waveform__data_source_on_get_data_format,
65892  ma_waveform__data_source_on_get_cursor,
65893  NULL, /* onGetLength. There's no notion of a length in waveforms. */
65894  NULL, /* onSetLooping */
65895  0
65896 };
65897 
65898 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)
65899 {
65900  ma_result result;
65901  ma_data_source_config dataSourceConfig;
65902 
65903  if (pWaveform == NULL) {
65904  return MA_INVALID_ARGS;
65905  }
65906 
65907  MA_ZERO_OBJECT(pWaveform);
65908 
65909  dataSourceConfig = ma_data_source_config_init();
65910  dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable;
65911 
65912  result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds);
65913  if (result != MA_SUCCESS) {
65914  return result;
65915  }
65916 
65917  pWaveform->config = *pConfig;
65918  pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
65919  pWaveform->time = 0;
65920 
65921  return MA_SUCCESS;
65922 }
65923 
65924 MA_API void ma_waveform_uninit(ma_waveform* pWaveform)
65925 {
65926  if (pWaveform == NULL) {
65927  return;
65928  }
65929 
65930  ma_data_source_uninit(&pWaveform->ds);
65931 }
65932 
65933 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
65934 {
65935  if (pWaveform == NULL) {
65936  return MA_INVALID_ARGS;
65937  }
65938 
65939  pWaveform->config.amplitude = amplitude;
65940  return MA_SUCCESS;
65941 }
65942 
65943 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
65944 {
65945  if (pWaveform == NULL) {
65946  return MA_INVALID_ARGS;
65947  }
65948 
65949  pWaveform->config.frequency = frequency;
65950  ma_waveform__update_advance(pWaveform);
65951 
65952  return MA_SUCCESS;
65953 }
65954 
65955 MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type)
65956 {
65957  if (pWaveform == NULL) {
65958  return MA_INVALID_ARGS;
65959  }
65960 
65961  pWaveform->config.type = type;
65962  return MA_SUCCESS;
65963 }
65964 
65965 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)
65966 {
65967  if (pWaveform == NULL) {
65968  return MA_INVALID_ARGS;
65969  }
65970 
65971  pWaveform->config.sampleRate = sampleRate;
65972  ma_waveform__update_advance(pWaveform);
65973 
65974  return MA_SUCCESS;
65975 }
65976 
65977 static float ma_waveform_sine_f32(double time, double amplitude)
65978 {
65979  return (float)(ma_sind(MA_TAU_D * time) * amplitude);
65980 }
65981 
65982 static ma_int16 ma_waveform_sine_s16(double time, double amplitude)
65983 {
65984  return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude));
65985 }
65986 
65987 static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude)
65988 {
65989  double f = time - (ma_int64)time;
65990  double r;
65991 
65992  if (f < dutyCycle) {
65993  r = amplitude;
65994  } else {
65995  r = -amplitude;
65996  }
65997 
65998  return (float)r;
65999 }
66000 
66001 static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude)
66002 {
66003  return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude));
66004 }
66005 
66006 static float ma_waveform_triangle_f32(double time, double amplitude)
66007 {
66008  double f = time - (ma_int64)time;
66009  double r;
66010 
66011  r = 2 * ma_abs(2 * (f - 0.5)) - 1;
66012 
66013  return (float)(r * amplitude);
66014 }
66015 
66016 static ma_int16 ma_waveform_triangle_s16(double time, double amplitude)
66017 {
66018  return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude));
66019 }
66020 
66021 static float ma_waveform_sawtooth_f32(double time, double amplitude)
66022 {
66023  double f = time - (ma_int64)time;
66024  double r;
66025 
66026  r = 2 * (f - 0.5);
66027 
66028  return (float)(r * amplitude);
66029 }
66030 
66031 static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude)
66032 {
66033  return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude));
66034 }
66035 
66036 static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
66037 {
66038  ma_uint64 iFrame;
66039  ma_uint64 iChannel;
66040  ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
66041  ma_uint32 bpf = bps * pWaveform->config.channels;
66042 
66043  MA_ASSERT(pWaveform != NULL);
66044  MA_ASSERT(pFramesOut != NULL);
66045 
66046  if (pWaveform->config.format == ma_format_f32) {
66047  float* pFramesOutF32 = (float*)pFramesOut;
66048  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66049  float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
66050  pWaveform->time += pWaveform->advance;
66051 
66052  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66053  pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
66054  }
66055  }
66056  } else if (pWaveform->config.format == ma_format_s16) {
66057  ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66058  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66059  ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude);
66060  pWaveform->time += pWaveform->advance;
66061 
66062  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66063  pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
66064  }
66065  }
66066  } else {
66067  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66068  float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
66069  pWaveform->time += pWaveform->advance;
66070 
66071  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66072  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66073  }
66074  }
66075  }
66076 }
66077 
66078 static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount)
66079 {
66080  ma_uint64 iFrame;
66081  ma_uint64 iChannel;
66082  ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
66083  ma_uint32 bpf = bps * pWaveform->config.channels;
66084 
66085  MA_ASSERT(pWaveform != NULL);
66086  MA_ASSERT(pFramesOut != NULL);
66087 
66088  if (pWaveform->config.format == ma_format_f32) {
66089  float* pFramesOutF32 = (float*)pFramesOut;
66090  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66091  float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
66092  pWaveform->time += pWaveform->advance;
66093 
66094  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66095  pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
66096  }
66097  }
66098  } else if (pWaveform->config.format == ma_format_s16) {
66099  ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66100  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66101  ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
66102  pWaveform->time += pWaveform->advance;
66103 
66104  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66105  pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
66106  }
66107  }
66108  } else {
66109  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66110  float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
66111  pWaveform->time += pWaveform->advance;
66112 
66113  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66114  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66115  }
66116  }
66117  }
66118 }
66119 
66120 static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
66121 {
66122  ma_uint64 iFrame;
66123  ma_uint64 iChannel;
66124  ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
66125  ma_uint32 bpf = bps * pWaveform->config.channels;
66126 
66127  MA_ASSERT(pWaveform != NULL);
66128  MA_ASSERT(pFramesOut != NULL);
66129 
66130  if (pWaveform->config.format == ma_format_f32) {
66131  float* pFramesOutF32 = (float*)pFramesOut;
66132  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66133  float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
66134  pWaveform->time += pWaveform->advance;
66135 
66136  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66137  pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
66138  }
66139  }
66140  } else if (pWaveform->config.format == ma_format_s16) {
66141  ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66142  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66143  ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude);
66144  pWaveform->time += pWaveform->advance;
66145 
66146  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66147  pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
66148  }
66149  }
66150  } else {
66151  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66152  float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
66153  pWaveform->time += pWaveform->advance;
66154 
66155  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66156  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66157  }
66158  }
66159  }
66160 }
66161 
66162 static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
66163 {
66164  ma_uint64 iFrame;
66165  ma_uint64 iChannel;
66166  ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
66167  ma_uint32 bpf = bps * pWaveform->config.channels;
66168 
66169  MA_ASSERT(pWaveform != NULL);
66170  MA_ASSERT(pFramesOut != NULL);
66171 
66172  if (pWaveform->config.format == ma_format_f32) {
66173  float* pFramesOutF32 = (float*)pFramesOut;
66174  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66175  float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
66176  pWaveform->time += pWaveform->advance;
66177 
66178  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66179  pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
66180  }
66181  }
66182  } else if (pWaveform->config.format == ma_format_s16) {
66183  ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66184  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66185  ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude);
66186  pWaveform->time += pWaveform->advance;
66187 
66188  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66189  pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
66190  }
66191  }
66192  } else {
66193  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66194  float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
66195  pWaveform->time += pWaveform->advance;
66196 
66197  for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
66198  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66199  }
66200  }
66201  }
66202 }
66203 
66204 MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
66205 {
66206  if (pFramesRead != NULL) {
66207  *pFramesRead = 0;
66208  }
66209 
66210  if (frameCount == 0) {
66211  return MA_INVALID_ARGS;
66212  }
66213 
66214  if (pWaveform == NULL) {
66215  return MA_INVALID_ARGS;
66216  }
66217 
66218  if (pFramesOut != NULL) {
66219  switch (pWaveform->config.type)
66220  {
66221  case ma_waveform_type_sine:
66222  {
66223  ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);
66224  } break;
66225 
66226  case ma_waveform_type_square:
66227  {
66228  ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount);
66229  } break;
66230 
66231  case ma_waveform_type_triangle:
66232  {
66233  ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);
66234  } break;
66235 
66236  case ma_waveform_type_sawtooth:
66237  {
66238  ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);
66239  } break;
66240 
66241  default: return MA_INVALID_OPERATION; /* Unknown waveform type. */
66242  }
66243  } else {
66244  pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
66245  }
66246 
66247  if (pFramesRead != NULL) {
66248  *pFramesRead = frameCount;
66249  }
66250 
66251  return MA_SUCCESS;
66252 }
66253 
66254 MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex)
66255 {
66256  if (pWaveform == NULL) {
66257  return MA_INVALID_ARGS;
66258  }
66259 
66260  pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */
66261 
66262  return MA_SUCCESS;
66263 }
66264 
66265 MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency)
66266 {
66267  ma_pulsewave_config config;
66268 
66269  MA_ZERO_OBJECT(&config);
66270  config.format = format;
66271  config.channels = channels;
66272  config.sampleRate = sampleRate;
66273  config.dutyCycle = dutyCycle;
66274  config.amplitude = amplitude;
66275  config.frequency = frequency;
66276 
66277  return config;
66278 }
66279 
66280 MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform)
66281 {
66282  ma_result result;
66283  ma_waveform_config config;
66284 
66285  if (pWaveform == NULL) {
66286  return MA_INVALID_ARGS;
66287  }
66288 
66289  MA_ZERO_OBJECT(pWaveform);
66290 
66291  config = ma_waveform_config_init(
66292  pConfig->format,
66293  pConfig->channels,
66294  pConfig->sampleRate,
66295  ma_waveform_type_square,
66296  pConfig->amplitude,
66297  pConfig->frequency
66298  );
66299 
66300  result = ma_waveform_init(&config, &pWaveform->waveform);
66301  ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle);
66302 
66303  return result;
66304 }
66305 
66306 MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform)
66307 {
66308  if (pWaveform == NULL) {
66309  return;
66310  }
66311 
66312  ma_waveform_uninit(&pWaveform->waveform);
66313 }
66314 
66315 MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
66316 {
66317  if (pFramesRead != NULL) {
66318  *pFramesRead = 0;
66319  }
66320 
66321  if (frameCount == 0) {
66322  return MA_INVALID_ARGS;
66323  }
66324 
66325  if (pWaveform == NULL) {
66326  return MA_INVALID_ARGS;
66327  }
66328 
66329  if (pFramesOut != NULL) {
66330  ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount);
66331  } else {
66332  pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
66333  }
66334 
66335  if (pFramesRead != NULL) {
66336  *pFramesRead = frameCount;
66337  }
66338 
66339  return MA_SUCCESS;
66340 }
66341 
66342 MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex)
66343 {
66344  if (pWaveform == NULL) {
66345  return MA_INVALID_ARGS;
66346  }
66347 
66348  ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex);
66349 
66350  return MA_SUCCESS;
66351 }
66352 
66353 MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude)
66354 {
66355  if (pWaveform == NULL) {
66356  return MA_INVALID_ARGS;
66357  }
66358 
66359  pWaveform->config.amplitude = amplitude;
66360  ma_waveform_set_amplitude(&pWaveform->waveform, amplitude);
66361 
66362  return MA_SUCCESS;
66363 }
66364 
66365 MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency)
66366 {
66367  if (pWaveform == NULL) {
66368  return MA_INVALID_ARGS;
66369  }
66370 
66371  pWaveform->config.frequency = frequency;
66372  ma_waveform_set_frequency(&pWaveform->waveform, frequency);
66373 
66374  return MA_SUCCESS;
66375 }
66376 
66377 MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate)
66378 {
66379  if (pWaveform == NULL) {
66380  return MA_INVALID_ARGS;
66381  }
66382 
66383  pWaveform->config.sampleRate = sampleRate;
66384  ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate);
66385 
66386  return MA_SUCCESS;
66387 }
66388 
66389 MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle)
66390 {
66391  if (pWaveform == NULL) {
66392  return MA_INVALID_ARGS;
66393  }
66394 
66395  pWaveform->config.dutyCycle = dutyCycle;
66396 
66397  return MA_SUCCESS;
66398 }
66399 
66400 
66401 
66402 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
66403 {
66404  ma_noise_config config;
66405  MA_ZERO_OBJECT(&config);
66406 
66407  config.format = format;
66408  config.channels = channels;
66409  config.type = type;
66410  config.seed = seed;
66411  config.amplitude = amplitude;
66412 
66413  if (config.seed == 0) {
66414  config.seed = MA_DEFAULT_LCG_SEED;
66415  }
66416 
66417  return config;
66418 }
66419 
66420 
66421 static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
66422 {
66423  return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead);
66424 }
66425 
66426 static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
66427 {
66428  /* No-op. Just pretend to be successful. */
66429  (void)pDataSource;
66430  (void)frameIndex;
66431  return MA_SUCCESS;
66432 }
66433 
66434 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)
66435 {
66436  ma_noise* pNoise = (ma_noise*)pDataSource;
66437 
66438  *pFormat = pNoise->config.format;
66439  *pChannels = pNoise->config.channels;
66440  *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */
66441  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels);
66442 
66443  return MA_SUCCESS;
66444 }
66445 
66446 static ma_data_source_vtable g_ma_noise_data_source_vtable =
66447 {
66448  ma_noise__data_source_on_read,
66449  ma_noise__data_source_on_seek, /* No-op for noise. */
66450  ma_noise__data_source_on_get_data_format,
66451  NULL, /* onGetCursor. No notion of a cursor for noise. */
66452  NULL, /* onGetLength. No notion of a length for noise. */
66453  NULL, /* onSetLooping */
66454  0
66455 };
66456 
66457 
66458 #ifndef MA_PINK_NOISE_BIN_SIZE
66459 #define MA_PINK_NOISE_BIN_SIZE 16
66460 #endif
66461 
66462 typedef struct
66463 {
66464  size_t sizeInBytes;
66465  struct
66466  {
66467  size_t binOffset;
66468  size_t accumulationOffset;
66469  size_t counterOffset;
66470  } pink;
66471  struct
66472  {
66473  size_t accumulationOffset;
66474  } brownian;
66475 } ma_noise_heap_layout;
66476 
66477 static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout)
66478 {
66479  MA_ASSERT(pHeapLayout != NULL);
66480 
66481  MA_ZERO_OBJECT(pHeapLayout);
66482 
66483  if (pConfig == NULL) {
66484  return MA_INVALID_ARGS;
66485  }
66486 
66487  if (pConfig->channels == 0) {
66488  return MA_INVALID_ARGS;
66489  }
66490 
66491  pHeapLayout->sizeInBytes = 0;
66492 
66493  /* Pink. */
66494  if (pConfig->type == ma_noise_type_pink) {
66495  /* bin */
66496  pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes;
66497  pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels;
66498  pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE;
66499 
66500  /* accumulation */
66501  pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes;
66502  pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
66503 
66504  /* counter */
66505  pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes;
66506  pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels;
66507  }
66508 
66509  /* Brownian. */
66510  if (pConfig->type == ma_noise_type_brownian) {
66511  /* accumulation */
66512  pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes;
66513  pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
66514  }
66515 
66516  /* Make sure allocation size is aligned. */
66517  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
66518 
66519  return MA_SUCCESS;
66520 }
66521 
66522 MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes)
66523 {
66524  ma_result result;
66525  ma_noise_heap_layout heapLayout;
66526 
66527  if (pHeapSizeInBytes == NULL) {
66528  return MA_INVALID_ARGS;
66529  }
66530 
66531  *pHeapSizeInBytes = 0;
66532 
66533  result = ma_noise_get_heap_layout(pConfig, &heapLayout);
66534  if (result != MA_SUCCESS) {
66535  return result;
66536  }
66537 
66538  *pHeapSizeInBytes = heapLayout.sizeInBytes;
66539 
66540  return MA_SUCCESS;
66541 }
66542 
66543 MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise)
66544 {
66545  ma_result result;
66546  ma_noise_heap_layout heapLayout;
66547  ma_data_source_config dataSourceConfig;
66548  ma_uint32 iChannel;
66549 
66550  if (pNoise == NULL) {
66551  return MA_INVALID_ARGS;
66552  }
66553 
66554  MA_ZERO_OBJECT(pNoise);
66555 
66556  result = ma_noise_get_heap_layout(pConfig, &heapLayout);
66557  if (result != MA_SUCCESS) {
66558  return result;
66559  }
66560 
66561  pNoise->_pHeap = pHeap;
66562  MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes);
66563 
66564  dataSourceConfig = ma_data_source_config_init();
66565  dataSourceConfig.vtable = &g_ma_noise_data_source_vtable;
66566 
66567  result = ma_data_source_init(&dataSourceConfig, &pNoise->ds);
66568  if (result != MA_SUCCESS) {
66569  return result;
66570  }
66571 
66572  pNoise->config = *pConfig;
66573  ma_lcg_seed(&pNoise->lcg, pConfig->seed);
66574 
66575  if (pNoise->config.type == ma_noise_type_pink) {
66576  pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset);
66577  pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset);
66578  pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset);
66579 
66580  for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
66581  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));
66582  pNoise->state.pink.accumulation[iChannel] = 0;
66583  pNoise->state.pink.counter[iChannel] = 1;
66584  }
66585  }
66586 
66587  if (pNoise->config.type == ma_noise_type_brownian) {
66588  pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset);
66589 
66590  for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
66591  pNoise->state.brownian.accumulation[iChannel] = 0;
66592  }
66593  }
66594 
66595  return MA_SUCCESS;
66596 }
66597 
66598 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise)
66599 {
66600  ma_result result;
66601  size_t heapSizeInBytes;
66602  void* pHeap;
66603 
66604  result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes);
66605  if (result != MA_SUCCESS) {
66606  return result;
66607  }
66608 
66609  if (heapSizeInBytes > 0) {
66610  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
66611  if (pHeap == NULL) {
66612  return MA_OUT_OF_MEMORY;
66613  }
66614  } else {
66615  pHeap = NULL;
66616  }
66617 
66618  result = ma_noise_init_preallocated(pConfig, pHeap, pNoise);
66619  if (result != MA_SUCCESS) {
66620  ma_free(pHeap, pAllocationCallbacks);
66621  return result;
66622  }
66623 
66624  pNoise->_ownsHeap = MA_TRUE;
66625  return MA_SUCCESS;
66626 }
66627 
66628 MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks)
66629 {
66630  if (pNoise == NULL) {
66631  return;
66632  }
66633 
66634  ma_data_source_uninit(&pNoise->ds);
66635 
66636  if (pNoise->_ownsHeap) {
66637  ma_free(pNoise->_pHeap, pAllocationCallbacks);
66638  }
66639 }
66640 
66641 MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude)
66642 {
66643  if (pNoise == NULL) {
66644  return MA_INVALID_ARGS;
66645  }
66646 
66647  pNoise->config.amplitude = amplitude;
66648  return MA_SUCCESS;
66649 }
66650 
66651 MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed)
66652 {
66653  if (pNoise == NULL) {
66654  return MA_INVALID_ARGS;
66655  }
66656 
66657  pNoise->lcg.state = seed;
66658  return MA_SUCCESS;
66659 }
66660 
66661 
66662 MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type)
66663 {
66664  if (pNoise == NULL) {
66665  return MA_INVALID_ARGS;
66666  }
66667 
66668  /*
66669  This function should never have been implemented in the first place. Changing the type dynamically is not
66670  supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function
66671  will be removed in version 0.12.
66672  */
66673  MA_ASSERT(MA_FALSE);
66674  (void)type;
66675 
66676  return MA_INVALID_OPERATION;
66677 }
66678 
66679 static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)
66680 {
66681  return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);
66682 }
66683 
66684 static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)
66685 {
66686  return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));
66687 }
66688 
66689 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
66690 {
66691  ma_uint64 iFrame;
66692  ma_uint32 iChannel;
66693  const ma_uint32 channels = pNoise->config.channels;
66694  MA_ASSUME(channels > 0);
66695 
66696  if (pNoise->config.format == ma_format_f32) {
66697  float* pFramesOutF32 = (float*)pFramesOut;
66698  if (pNoise->config.duplicateChannels) {
66699  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66700  float s = ma_noise_f32_white(pNoise);
66701  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66702  pFramesOutF32[iFrame*channels + iChannel] = s;
66703  }
66704  }
66705  } else {
66706  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66707  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66708  pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise);
66709  }
66710  }
66711  }
66712  } else if (pNoise->config.format == ma_format_s16) {
66713  ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66714  if (pNoise->config.duplicateChannels) {
66715  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66716  ma_int16 s = ma_noise_s16_white(pNoise);
66717  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66718  pFramesOutS16[iFrame*channels + iChannel] = s;
66719  }
66720  }
66721  } else {
66722  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66723  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66724  pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise);
66725  }
66726  }
66727  }
66728  } else {
66729  const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
66730  const ma_uint32 bpf = bps * channels;
66731 
66732  if (pNoise->config.duplicateChannels) {
66733  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66734  float s = ma_noise_f32_white(pNoise);
66735  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66736  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66737  }
66738  }
66739  } else {
66740  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66741  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66742  float s = ma_noise_f32_white(pNoise);
66743  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66744  }
66745  }
66746  }
66747  }
66748 
66749  return frameCount;
66750 }
66751 
66752 
66753 static MA_INLINE unsigned int ma_tzcnt32(unsigned int x)
66754 {
66755  unsigned int n;
66756 
66757  /* Special case for odd numbers since they should happen about half the time. */
66758  if (x & 0x1) {
66759  return 0;
66760  }
66761 
66762  if (x == 0) {
66763  return sizeof(x) << 3;
66764  }
66765 
66766  n = 1;
66767  if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }
66768  if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; }
66769  if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; }
66770  if ((x & 0x00000003) == 0) { x >>= 2; n += 2; }
66771  n -= x & 0x00000001;
66772 
66773  return n;
66774 }
66775 
66776 /*
66777 Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h
66778 
66779 This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/
66780 */
66781 static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)
66782 {
66783  double result;
66784  double binPrev;
66785  double binNext;
66786  unsigned int ibin;
66787 
66788  ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1);
66789 
66790  binPrev = pNoise->state.pink.bin[iChannel][ibin];
66791  binNext = ma_lcg_rand_f64(&pNoise->lcg);
66792  pNoise->state.pink.bin[iChannel][ibin] = binNext;
66793 
66794  pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);
66795  pNoise->state.pink.counter[iChannel] += 1;
66796 
66797  result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);
66798  result /= 10;
66799 
66800  return (float)(result * pNoise->config.amplitude);
66801 }
66802 
66803 static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)
66804 {
66805  return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));
66806 }
66807 
66808 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
66809 {
66810  ma_uint64 iFrame;
66811  ma_uint32 iChannel;
66812  const ma_uint32 channels = pNoise->config.channels;
66813  MA_ASSUME(channels > 0);
66814 
66815  if (pNoise->config.format == ma_format_f32) {
66816  float* pFramesOutF32 = (float*)pFramesOut;
66817  if (pNoise->config.duplicateChannels) {
66818  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66819  float s = ma_noise_f32_pink(pNoise, 0);
66820  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66821  pFramesOutF32[iFrame*channels + iChannel] = s;
66822  }
66823  }
66824  } else {
66825  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66826  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66827  pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);
66828  }
66829  }
66830  }
66831  } else if (pNoise->config.format == ma_format_s16) {
66832  ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66833  if (pNoise->config.duplicateChannels) {
66834  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66835  ma_int16 s = ma_noise_s16_pink(pNoise, 0);
66836  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66837  pFramesOutS16[iFrame*channels + iChannel] = s;
66838  }
66839  }
66840  } else {
66841  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66842  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66843  pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);
66844  }
66845  }
66846  }
66847  } else {
66848  const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
66849  const ma_uint32 bpf = bps * channels;
66850 
66851  if (pNoise->config.duplicateChannels) {
66852  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66853  float s = ma_noise_f32_pink(pNoise, 0);
66854  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66855  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66856  }
66857  }
66858  } else {
66859  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66860  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66861  float s = ma_noise_f32_pink(pNoise, iChannel);
66862  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66863  }
66864  }
66865  }
66866  }
66867 
66868  return frameCount;
66869 }
66870 
66871 
66872 static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
66873 {
66874  double result;
66875 
66876  result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
66877  result /= 1.005; /* Don't escape the -1..1 range on average. */
66878 
66879  pNoise->state.brownian.accumulation[iChannel] = result;
66880  result /= 20;
66881 
66882  return (float)(result * pNoise->config.amplitude);
66883 }
66884 
66885 static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
66886 {
66887  return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
66888 }
66889 
66890 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
66891 {
66892  ma_uint64 iFrame;
66893  ma_uint32 iChannel;
66894  const ma_uint32 channels = pNoise->config.channels;
66895  MA_ASSUME(channels > 0);
66896 
66897  if (pNoise->config.format == ma_format_f32) {
66898  float* pFramesOutF32 = (float*)pFramesOut;
66899  if (pNoise->config.duplicateChannels) {
66900  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66901  float s = ma_noise_f32_brownian(pNoise, 0);
66902  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66903  pFramesOutF32[iFrame*channels + iChannel] = s;
66904  }
66905  }
66906  } else {
66907  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66908  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66909  pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);
66910  }
66911  }
66912  }
66913  } else if (pNoise->config.format == ma_format_s16) {
66914  ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66915  if (pNoise->config.duplicateChannels) {
66916  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66917  ma_int16 s = ma_noise_s16_brownian(pNoise, 0);
66918  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66919  pFramesOutS16[iFrame*channels + iChannel] = s;
66920  }
66921  }
66922  } else {
66923  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66924  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66925  pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);
66926  }
66927  }
66928  }
66929  } else {
66930  const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
66931  const ma_uint32 bpf = bps * channels;
66932 
66933  if (pNoise->config.duplicateChannels) {
66934  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66935  float s = ma_noise_f32_brownian(pNoise, 0);
66936  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66937  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66938  }
66939  }
66940  } else {
66941  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66942  for (iChannel = 0; iChannel < channels; iChannel += 1) {
66943  float s = ma_noise_f32_brownian(pNoise, iChannel);
66944  ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66945  }
66946  }
66947  }
66948  }
66949 
66950  return frameCount;
66951 }
66952 
66953 MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
66954 {
66955  ma_uint64 framesRead = 0;
66956 
66957  if (pFramesRead != NULL) {
66958  *pFramesRead = 0;
66959  }
66960 
66961  if (frameCount == 0) {
66962  return MA_INVALID_ARGS;
66963  }
66964 
66965  if (pNoise == NULL) {
66966  return MA_INVALID_ARGS;
66967  }
66968 
66969  /* 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. */
66970  if (pFramesOut == NULL) {
66971  framesRead = frameCount;
66972  } else {
66973  switch (pNoise->config.type) {
66974  case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break;
66975  case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break;
66976  case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break;
66977  default: return MA_INVALID_OPERATION; /* Unknown noise type. */
66978  }
66979  }
66980 
66981  if (pFramesRead != NULL) {
66982  *pFramesRead = framesRead;
66983  }
66984 
66985  return MA_SUCCESS;
66986 }
66987 #endif /* MA_NO_GENERATION */
66988 
66989 
66990 
66991 #ifndef MA_NO_RESOURCE_MANAGER
66992 #ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS
66993 #define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000
66994 #endif
66995 
66996 #ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY
66997 #define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024
66998 #endif
66999 
67000 MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void)
67001 {
67003 
67004  MA_ZERO_OBJECT(&notifications);
67005 
67006  return notifications;
67007 }
67008 
67009 static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
67010 {
67011  if (pPipelineNotifications == NULL) {
67012  return;
67013  }
67014 
67015  if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); }
67016  if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); }
67017 }
67018 
67019 static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
67020 {
67021  if (pPipelineNotifications == NULL) {
67022  return;
67023  }
67024 
67025  if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); }
67026  if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); }
67027 }
67028 
67029 static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
67030 {
67031  if (pPipelineNotifications == NULL) {
67032  return;
67033  }
67034 
67035  if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); }
67036  if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); }
67037 }
67038 
67039 
67040 
67041 #ifndef MA_DEFAULT_HASH_SEED
67042 #define MA_DEFAULT_HASH_SEED 42
67043 #endif
67044 
67045 /* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */
67046 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
67047  #pragma GCC diagnostic push
67048  #if __GNUC__ >= 7
67049  #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
67050  #endif
67051 #endif
67052 
67053 static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r)
67054 {
67055  return (x << r) | (x >> (32 - r));
67056 }
67057 
67058 static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i)
67059 {
67060  ma_uint32 block;
67061 
67062  /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */
67063  MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block));
67064 
67065  if (ma_is_little_endian()) {
67066  return block;
67067  } else {
67068  return ma_swap_endian_uint32(block);
67069  }
67070 }
67071 
67072 static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h)
67073 {
67074  h ^= h >> 16;
67075  h *= 0x85ebca6b;
67076  h ^= h >> 13;
67077  h *= 0xc2b2ae35;
67078  h ^= h >> 16;
67079 
67080  return h;
67081 }
67082 
67083 static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed)
67084 {
67085  const ma_uint8* data = (const ma_uint8*)key;
67086  const ma_uint32* blocks;
67087  const ma_uint8* tail;
67088  const int nblocks = len / 4;
67089  ma_uint32 h1 = seed;
67090  ma_uint32 c1 = 0xcc9e2d51;
67091  ma_uint32 c2 = 0x1b873593;
67092  ma_uint32 k1;
67093  int i;
67094 
67095  blocks = (const ma_uint32 *)(data + nblocks*4);
67096 
67097  for(i = -nblocks; i; i++) {
67098  k1 = ma_hash_getblock(blocks,i);
67099 
67100  k1 *= c1;
67101  k1 = ma_rotl32(k1, 15);
67102  k1 *= c2;
67103 
67104  h1 ^= k1;
67105  h1 = ma_rotl32(h1, 13);
67106  h1 = h1*5 + 0xe6546b64;
67107  }
67108 
67109 
67110  tail = (const ma_uint8*)(data + nblocks*4);
67111 
67112  k1 = 0;
67113  switch(len & 3) {
67114  case 3: k1 ^= tail[2] << 16;
67115  case 2: k1 ^= tail[1] << 8;
67116  case 1: k1 ^= tail[0];
67117  k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1;
67118  };
67119 
67120 
67121  h1 ^= len;
67122  h1 = ma_hash_fmix32(h1);
67123 
67124  return h1;
67125 }
67126 
67127 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
67128  #pragma GCC diagnostic push
67129 #endif
67130 /* End MurmurHash3 */
67131 
67132 static ma_uint32 ma_hash_string_32(const char* str)
67133 {
67134  return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED);
67135 }
67136 
67137 static ma_uint32 ma_hash_string_w_32(const wchar_t* str)
67138 {
67139  return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED);
67140 }
67141 
67142 
67143 
67144 
67145 /*
67146 Basic BST Functions
67147 */
67148 static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode)
67149 {
67151 
67152  MA_ASSERT(pResourceManager != NULL);
67153  MA_ASSERT(ppDataBufferNode != NULL);
67154 
67155  pCurrentNode = pResourceManager->pRootDataBufferNode;
67156  while (pCurrentNode != NULL) {
67157  if (hashedName32 == pCurrentNode->hashedName32) {
67158  break; /* Found. */
67159  } else if (hashedName32 < pCurrentNode->hashedName32) {
67160  pCurrentNode = pCurrentNode->pChildLo;
67161  } else {
67162  pCurrentNode = pCurrentNode->pChildHi;
67163  }
67164  }
67165 
67166  *ppDataBufferNode = pCurrentNode;
67167 
67168  if (pCurrentNode == NULL) {
67169  return MA_DOES_NOT_EXIST;
67170  } else {
67171  return MA_SUCCESS;
67172  }
67173 }
67174 
67175 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)
67176 {
67177  ma_result result = MA_SUCCESS;
67179 
67180  MA_ASSERT(pResourceManager != NULL);
67181  MA_ASSERT(ppInsertPoint != NULL);
67182 
67183  *ppInsertPoint = NULL;
67184 
67185  if (pResourceManager->pRootDataBufferNode == NULL) {
67186  return MA_SUCCESS; /* No items. */
67187  }
67188 
67189  /* 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. */
67190  pCurrentNode = pResourceManager->pRootDataBufferNode;
67191  while (pCurrentNode != NULL) {
67192  if (hashedName32 == pCurrentNode->hashedName32) {
67193  result = MA_ALREADY_EXISTS;
67194  break;
67195  } else {
67196  if (hashedName32 < pCurrentNode->hashedName32) {
67197  if (pCurrentNode->pChildLo == NULL) {
67198  result = MA_SUCCESS;
67199  break;
67200  } else {
67201  pCurrentNode = pCurrentNode->pChildLo;
67202  }
67203  } else {
67204  if (pCurrentNode->pChildHi == NULL) {
67205  result = MA_SUCCESS;
67206  break;
67207  } else {
67208  pCurrentNode = pCurrentNode->pChildHi;
67209  }
67210  }
67211  }
67212  }
67213 
67214  *ppInsertPoint = pCurrentNode;
67215  return result;
67216 }
67217 
67218 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)
67219 {
67220  MA_ASSERT(pResourceManager != NULL);
67221  MA_ASSERT(pDataBufferNode != NULL);
67222 
67223  /* The key must have been set before calling this function. */
67224  MA_ASSERT(pDataBufferNode->hashedName32 != 0);
67225 
67226  if (pInsertPoint == NULL) {
67227  /* It's the first node. */
67228  pResourceManager->pRootDataBufferNode = pDataBufferNode;
67229  } else {
67230  /* It's not the first node. It needs to be inserted. */
67231  if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) {
67232  MA_ASSERT(pInsertPoint->pChildLo == NULL);
67233  pInsertPoint->pChildLo = pDataBufferNode;
67234  } else {
67235  MA_ASSERT(pInsertPoint->pChildHi == NULL);
67236  pInsertPoint->pChildHi = pDataBufferNode;
67237  }
67238  }
67239 
67240  pDataBufferNode->pParent = pInsertPoint;
67241 
67242  return MA_SUCCESS;
67243 }
67244 
67245 #if 0 /* Unused for now. */
67246 static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
67247 {
67248  ma_result result;
67250 
67251  MA_ASSERT(pResourceManager != NULL);
67252  MA_ASSERT(pDataBufferNode != NULL);
67253 
67254  result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint);
67255  if (result != MA_SUCCESS) {
67256  return MA_INVALID_ARGS;
67257  }
67258 
67259  return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
67260 }
67261 #endif
67262 
67263 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode)
67264 {
67266 
67267  MA_ASSERT(pDataBufferNode != NULL);
67268 
67269  pCurrentNode = pDataBufferNode;
67270  while (pCurrentNode->pChildLo != NULL) {
67271  pCurrentNode = pCurrentNode->pChildLo;
67272  }
67273 
67274  return pCurrentNode;
67275 }
67276 
67277 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode)
67278 {
67280 
67281  MA_ASSERT(pDataBufferNode != NULL);
67282 
67283  pCurrentNode = pDataBufferNode;
67284  while (pCurrentNode->pChildHi != NULL) {
67285  pCurrentNode = pCurrentNode->pChildHi;
67286  }
67287 
67288  return pCurrentNode;
67289 }
67290 
67291 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)
67292 {
67293  MA_ASSERT(pDataBufferNode != NULL);
67294  MA_ASSERT(pDataBufferNode->pChildHi != NULL);
67295 
67296  return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi);
67297 }
67298 
67299 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)
67300 {
67301  MA_ASSERT(pDataBufferNode != NULL);
67302  MA_ASSERT(pDataBufferNode->pChildLo != NULL);
67303 
67304  return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo);
67305 }
67306 
67307 static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
67308 {
67309  MA_ASSERT(pResourceManager != NULL);
67310  MA_ASSERT(pDataBufferNode != NULL);
67311 
67312  if (pDataBufferNode->pChildLo == NULL) {
67313  if (pDataBufferNode->pChildHi == NULL) {
67314  /* Simple case - deleting a buffer with no children. */
67315  if (pDataBufferNode->pParent == NULL) {
67316  MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */
67317  pResourceManager->pRootDataBufferNode = NULL;
67318  } else {
67319  if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
67320  pDataBufferNode->pParent->pChildLo = NULL;
67321  } else {
67322  pDataBufferNode->pParent->pChildHi = NULL;
67323  }
67324  }
67325  } else {
67326  /* Node has one child - pChildHi != NULL. */
67327  pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent;
67328 
67329  if (pDataBufferNode->pParent == NULL) {
67330  MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
67331  pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi;
67332  } else {
67333  if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
67334  pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi;
67335  } else {
67336  pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi;
67337  }
67338  }
67339  }
67340  } else {
67341  if (pDataBufferNode->pChildHi == NULL) {
67342  /* Node has one child - pChildLo != NULL. */
67343  pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent;
67344 
67345  if (pDataBufferNode->pParent == NULL) {
67346  MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
67347  pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo;
67348  } else {
67349  if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
67350  pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo;
67351  } else {
67352  pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo;
67353  }
67354  }
67355  } else {
67356  /* Complex case - deleting a node with two children. */
67357  ma_resource_manager_data_buffer_node* pReplacementDataBufferNode;
67358 
67359  /* 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. */
67360  pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode);
67361  MA_ASSERT(pReplacementDataBufferNode != NULL);
67362 
67363  /*
67364  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
67365  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
67366  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
67367  replacement node and reinserting it into the same position as the deleted node.
67368  */
67369  MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */
67370  MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */
67371 
67372  if (pReplacementDataBufferNode->pChildHi == NULL) {
67373  if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
67374  pReplacementDataBufferNode->pParent->pChildLo = NULL;
67375  } else {
67376  pReplacementDataBufferNode->pParent->pChildHi = NULL;
67377  }
67378  } else {
67379  pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent;
67380  if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
67381  pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi;
67382  } else {
67383  pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi;
67384  }
67385  }
67386 
67387 
67388  /* 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 */
67389  if (pDataBufferNode->pParent != NULL) {
67390  if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
67391  pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode;
67392  } else {
67393  pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode;
67394  }
67395  }
67396 
67397  /* Now need to update the replacement node's pointers. */
67398  pReplacementDataBufferNode->pParent = pDataBufferNode->pParent;
67399  pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo;
67400  pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi;
67401 
67402  /* Now the children of the replacement node need to have their parent pointers updated. */
67403  if (pReplacementDataBufferNode->pChildLo != NULL) {
67404  pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode;
67405  }
67406  if (pReplacementDataBufferNode->pChildHi != NULL) {
67407  pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode;
67408  }
67409 
67410  /* Now the root node needs to be updated. */
67411  if (pResourceManager->pRootDataBufferNode == pDataBufferNode) {
67412  pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode;
67413  }
67414  }
67415  }
67416 
67417  return MA_SUCCESS;
67418 }
67419 
67420 #if 0 /* Unused for now. */
67421 static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32)
67422 {
67423  ma_result result;
67424  ma_resource_manager_data_buffer_node* pDataBufferNode;
67425 
67426  result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode);
67427  if (result != MA_SUCCESS) {
67428  return result; /* Could not find the data buffer. */
67429  }
67430 
67431  return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode);
67432 }
67433 #endif
67434 
67435 static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode)
67436 {
67437  return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type);
67438 }
67439 
67440 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)
67441 {
67442  ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType);
67443 }
67444 
67445 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)
67446 {
67447  ma_uint32 refCount;
67448 
67449  MA_ASSERT(pResourceManager != NULL);
67450  MA_ASSERT(pDataBufferNode != NULL);
67451 
67452  (void)pResourceManager;
67453 
67454  refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1;
67455 
67456  if (pNewRefCount != NULL) {
67457  *pNewRefCount = refCount;
67458  }
67459 
67460  return MA_SUCCESS;
67461 }
67462 
67463 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)
67464 {
67465  ma_uint32 refCount;
67466 
67467  MA_ASSERT(pResourceManager != NULL);
67468  MA_ASSERT(pDataBufferNode != NULL);
67469 
67470  (void)pResourceManager;
67471 
67472  refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1;
67473 
67474  if (pNewRefCount != NULL) {
67475  *pNewRefCount = refCount;
67476  }
67477 
67478  return MA_SUCCESS;
67479 }
67480 
67481 static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
67482 {
67483  MA_ASSERT(pResourceManager != NULL);
67484  MA_ASSERT(pDataBufferNode != NULL);
67485 
67486  if (pDataBufferNode->isDataOwnedByResourceManager) {
67487  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) {
67488  ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks);
67489  pDataBufferNode->data.backend.encoded.pData = NULL;
67490  pDataBufferNode->data.backend.encoded.sizeInBytes = 0;
67491  } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) {
67492  ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks);
67493  pDataBufferNode->data.backend.decoded.pData = NULL;
67494  pDataBufferNode->data.backend.decoded.totalFrameCount = 0;
67495  } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) {
67496  ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks);
67497  } else {
67498  /* Should never hit this if the node was successfully initialized. */
67499  MA_ASSERT(pDataBufferNode->result != MA_SUCCESS);
67500  }
67501  }
67502 
67503  /* The data buffer itself needs to be freed. */
67504  ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
67505 }
67506 
67507 static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode)
67508 {
67509  MA_ASSERT(pDataBufferNode != NULL);
67510 
67511  return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */
67512 }
67513 
67514 
67515 static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager)
67516 {
67517  MA_ASSERT(pResourceManager != NULL);
67518 
67519  return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0;
67520 }
67521 
67522 
67523 typedef struct
67524 {
67525  union
67526  {
67529  } backend; /* Must be the first member. */
67530  ma_resource_manager* pResourceManager;
67531 } ma_resource_manager_inline_notification;
67532 
67533 static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification)
67534 {
67535  MA_ASSERT(pResourceManager != NULL);
67536  MA_ASSERT(pNotification != NULL);
67537 
67538  pNotification->pResourceManager = pResourceManager;
67539 
67540  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67541  return ma_async_notification_event_init(&pNotification->backend.e);
67542  } else {
67543  return ma_async_notification_poll_init(&pNotification->backend.p);
67544  }
67545 }
67546 
67547 static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification)
67548 {
67549  MA_ASSERT(pNotification != NULL);
67550 
67551  if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
67552  ma_async_notification_event_uninit(&pNotification->backend.e);
67553  } else {
67554  /* No need to uninitialize a polling notification. */
67555  }
67556 }
67557 
67558 static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification)
67559 {
67560  MA_ASSERT(pNotification != NULL);
67561 
67562  if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
67563  ma_async_notification_event_wait(&pNotification->backend.e);
67564  } else {
67565  while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) {
67566  ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager);
67567  if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
67568  break;
67569  }
67570  }
67571  }
67572 }
67573 
67574 static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification)
67575 {
67576  ma_resource_manager_inline_notification_wait(pNotification);
67577  ma_resource_manager_inline_notification_uninit(pNotification);
67578 }
67579 
67580 
67581 static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager)
67582 {
67583  MA_ASSERT(pResourceManager != NULL);
67584 
67585  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67586  #ifndef MA_NO_THREADING
67587  {
67588  ma_mutex_lock(&pResourceManager->dataBufferBSTLock);
67589  }
67590  #else
67591  {
67592  MA_ASSERT(MA_FALSE); /* Should never hit this. */
67593  }
67594  #endif
67595  } else {
67596  /* Threading not enabled. Do nothing. */
67597  }
67598 }
67599 
67600 static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager)
67601 {
67602  MA_ASSERT(pResourceManager != NULL);
67603 
67604  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67605  #ifndef MA_NO_THREADING
67606  {
67607  ma_mutex_unlock(&pResourceManager->dataBufferBSTLock);
67608  }
67609  #else
67610  {
67611  MA_ASSERT(MA_FALSE); /* Should never hit this. */
67612  }
67613  #endif
67614  } else {
67615  /* Threading not enabled. Do nothing. */
67616  }
67617 }
67618 
67619 #ifndef MA_NO_THREADING
67620 static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData)
67621 {
67622  ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
67623  MA_ASSERT(pResourceManager != NULL);
67624 
67625  for (;;) {
67626  ma_result result;
67627  ma_job job;
67628 
67629  result = ma_resource_manager_next_job(pResourceManager, &job);
67630  if (result != MA_SUCCESS) {
67631  break;
67632  }
67633 
67634  /* Terminate if we got a quit message. */
67635  if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
67636  break;
67637  }
67638 
67639  ma_job_process(&job);
67640  }
67641 
67642  return (ma_thread_result)0;
67643 }
67644 #endif
67645 
67646 MA_API ma_resource_manager_config ma_resource_manager_config_init(void)
67647 {
67649 
67650  MA_ZERO_OBJECT(&config);
67651  config.decodedFormat = ma_format_unknown;
67652  config.decodedChannels = 0;
67653  config.decodedSampleRate = 0;
67654  config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */
67655  config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY;
67656 
67657  /* Flags. */
67658  config.flags = 0;
67659  #ifdef MA_NO_THREADING
67660  {
67661  /* Threading is disabled at compile time so disable threading at runtime as well by default. */
67662  config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
67663  config.jobThreadCount = 0;
67664  }
67665  #endif
67666 
67667  return config;
67668 }
67669 
67670 
67671 MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager)
67672 {
67673  ma_result result;
67674  ma_job_queue_config jobQueueConfig;
67675 
67676  if (pResourceManager == NULL) {
67677  return MA_INVALID_ARGS;
67678  }
67679 
67680  MA_ZERO_OBJECT(pResourceManager);
67681 
67682  if (pConfig == NULL) {
67683  return MA_INVALID_ARGS;
67684  }
67685 
67686  #ifndef MA_NO_THREADING
67687  {
67688  if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) {
67689  return MA_INVALID_ARGS; /* Requesting too many job threads. */
67690  }
67691  }
67692  #endif
67693 
67694  pResourceManager->config = *pConfig;
67695  ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks);
67696 
67697  /* Get the log set up early so we can start using it as soon as possible. */
67698  if (pResourceManager->config.pLog == NULL) {
67699  result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log);
67700  if (result == MA_SUCCESS) {
67701  pResourceManager->config.pLog = &pResourceManager->log;
67702  } else {
67703  pResourceManager->config.pLog = NULL; /* Logging is unavailable. */
67704  }
67705  }
67706 
67707  if (pResourceManager->config.pVFS == NULL) {
67708  result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks);
67709  if (result != MA_SUCCESS) {
67710  return result; /* Failed to initialize the default file system. */
67711  }
67712 
67713  pResourceManager->config.pVFS = &pResourceManager->defaultVFS;
67714  }
67715 
67716  /* If threading has been disabled at compile time, enfore it at run time as well. */
67717  #ifdef MA_NO_THREADING
67718  {
67719  pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
67720  }
67721  #endif
67722 
67723  /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */
67724  if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
67725  pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING;
67726 
67727  /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */
67728  if (pResourceManager->config.jobThreadCount > 0) {
67729  return MA_INVALID_ARGS;
67730  }
67731  }
67732 
67733  /* Job queue. */
67734  jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity;
67735  jobQueueConfig.flags = 0;
67736  if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) {
67737  if (pResourceManager->config.jobThreadCount > 0) {
67738  return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */
67739  }
67740 
67741  jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING;
67742  }
67743 
67744  result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue);
67745  if (result != MA_SUCCESS) {
67746  return result;
67747  }
67748 
67749 
67750  /* Custom decoding backends. */
67751  if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {
67752  size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;
67753 
67754  pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
67755  if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {
67756  ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
67757  return MA_OUT_OF_MEMORY;
67758  }
67759 
67760  MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
67761 
67762  pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount;
67763  pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData;
67764  }
67765 
67766 
67767 
67768  /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */
67769  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67770  #ifndef MA_NO_THREADING
67771  {
67772  ma_uint32 iJobThread;
67773 
67774  /* Data buffer lock. */
67775  result = ma_mutex_init(&pResourceManager->dataBufferBSTLock);
67776  if (result != MA_SUCCESS) {
67777  ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
67778  return result;
67779  }
67780 
67781  /* Create the job threads last to ensure the threads has access to valid data. */
67782  for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
67783  result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks);
67784  if (result != MA_SUCCESS) {
67785  ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
67786  ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
67787  return result;
67788  }
67789  }
67790  }
67791  #else
67792  {
67793  /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */
67794  MA_ASSERT(MA_FALSE);
67795  }
67796  #endif
67797  }
67798 
67799  return MA_SUCCESS;
67800 }
67801 
67802 
67803 static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager)
67804 {
67805  MA_ASSERT(pResourceManager);
67806 
67807  /* If everything was done properly, there shouldn't be any active data buffers. */
67808  while (pResourceManager->pRootDataBufferNode != NULL) {
67809  ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;
67810  ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
67811 
67812  /* The data buffer has been removed from the BST, so now we need to free it's data. */
67813  ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
67814  }
67815 }
67816 
67817 MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
67818 {
67819  if (pResourceManager == NULL) {
67820  return;
67821  }
67822 
67823  /*
67824  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
67825  queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it.
67826  */
67827  ma_resource_manager_post_job_quit(pResourceManager);
67828 
67829  /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */
67830  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67831  #ifndef MA_NO_THREADING
67832  {
67833  ma_uint32 iJobThread;
67834 
67835  for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
67836  ma_thread_wait(&pResourceManager->jobThreads[iJobThread]);
67837  }
67838  }
67839  #else
67840  {
67841  MA_ASSERT(MA_FALSE); /* Should never hit this. */
67842  }
67843  #endif
67844  }
67845 
67846  /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */
67847  ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager);
67848 
67849  /* The job queue is no longer needed. */
67850  ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
67851 
67852  /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */
67853  if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67854  #ifndef MA_NO_THREADING
67855  {
67856  ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
67857  }
67858  #else
67859  {
67860  MA_ASSERT(MA_FALSE); /* Should never hit this. */
67861  }
67862  #endif
67863  }
67864 
67865  ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);
67866 
67867  if (pResourceManager->config.pLog == &pResourceManager->log) {
67868  ma_log_uninit(&pResourceManager->log);
67869  }
67870 }
67871 
67872 MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager)
67873 {
67874  if (pResourceManager == NULL) {
67875  return NULL;
67876  }
67877 
67878  return pResourceManager->config.pLog;
67879 }
67880 
67881 
67882 
67883 MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void)
67884 {
67886 
67887  MA_ZERO_OBJECT(&config);
67888  config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
67889  config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
67890  config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;
67891  config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;
67892  config.isLooping = MA_FALSE;
67893 
67894  return config;
67895 }
67896 
67897 
67898 static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager)
67899 {
67900  ma_decoder_config config;
67901 
67902  config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);
67903  config.allocationCallbacks = pResourceManager->config.allocationCallbacks;
67904  config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables;
67905  config.customBackendCount = pResourceManager->config.customDecodingBackendCount;
67906  config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData;
67907 
67908  return config;
67909 }
67910 
67911 static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder)
67912 {
67913  ma_result result;
67914  ma_decoder_config config;
67915 
67916  MA_ASSERT(pResourceManager != NULL);
67917  MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
67918  MA_ASSERT(pDecoder != NULL);
67919 
67920  config = ma_resource_manager__init_decoder_config(pResourceManager);
67921 
67922  if (pFilePath != NULL) {
67923  result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder);
67924  if (result != MA_SUCCESS) {
67925  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));
67926  return result;
67927  }
67928  } else {
67929  result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder);
67930  if (result != MA_SUCCESS) {
67931  #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
67932  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));
67933  #endif
67934  return result;
67935  }
67936  }
67937 
67938  return MA_SUCCESS;
67939 }
67940 
67941 static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer)
67942 {
67943  return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized);
67944 }
67945 
67946 static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer)
67947 {
67948  if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
67949  return NULL; /* Connector not yet initialized. */
67950  }
67951 
67952  switch (pDataBuffer->pNode->data.type)
67953  {
67954  case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder;
67955  case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer;
67956  case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer;
67957 
67958  case ma_resource_manager_data_supply_type_unknown:
67959  default:
67960  {
67961  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");
67962  return NULL;
67963  };
67964  };
67965 }
67966 
67967 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)
67968 {
67969  ma_result result;
67970 
67971  MA_ASSERT(pDataBuffer != NULL);
67972  MA_ASSERT(pConfig != NULL);
67973  MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE);
67974 
67975  /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */
67976  result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
67977  if (result != MA_SUCCESS && result != MA_BUSY) {
67978  return result; /* The data buffer is in an erroneous state. */
67979  }
67980 
67981  /*
67982  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
67983  "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
67984  an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead.
67985  */
67986  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
67987  {
67988  case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
67989  {
67990  ma_decoder_config config;
67991  config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager);
67992  result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder);
67993  } break;
67994 
67995  case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
67996  {
67997  ma_audio_buffer_config config;
67998  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);
67999  result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);
68000  } break;
68001 
68002  case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
68003  {
68005  config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data);
68006  result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer);
68007  } break;
68008 
68009  case ma_resource_manager_data_supply_type_unknown:
68010  default:
68011  {
68012  /* Unknown data supply type. Should never happen. Need to post an error here. */
68013  return MA_INVALID_ARGS;
68014  };
68015  }
68016 
68017  /*
68018  Initialization of the connector is when we can fire the init notification. This will give the application access to
68019  the format/channels/rate of the data source.
68020  */
68021  if (result == MA_SUCCESS) {
68022  /*
68023  The resource manager supports the ability to set the range and loop settings via a config at
68024  initialization time. This results in an case where the ranges could be set explicitly via
68025  ma_data_source_set_*() before we get to this point here. If this happens, we'll end up
68026  hitting a case where we just override those settings which results in what feels like a bug.
68027 
68028  To address this we only change the relevant properties if they're not equal to defaults. If
68029  they're equal to defaults there's no need to change them anyway. If they're *not* set to the
68030  default values, we can assume the user has set the range and loop settings via the config. If
68031  they're doing their own calls to ma_data_source_set_*() in addition to setting them via the
68032  config, that's entirely on the caller and any synchronization issue becomes their problem.
68033  */
68034  if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) {
68035  ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
68036  }
68037 
68038  if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) {
68039  ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
68040  }
68041 
68042  if (pConfig->isLooping != MA_FALSE) {
68043  ma_data_source_set_looping(pDataBuffer, pConfig->isLooping);
68044  }
68045 
68046  ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE);
68047 
68048  if (pInitNotification != NULL) {
68049  ma_async_notification_signal(pInitNotification);
68050  }
68051 
68052  if (pInitFence != NULL) {
68053  ma_fence_release(pInitFence);
68054  }
68055  }
68056 
68057  /* 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. */
68058  return result;
68059 }
68060 
68061 static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer)
68062 {
68063  MA_ASSERT(pResourceManager != NULL);
68064  MA_ASSERT(pDataBuffer != NULL);
68065 
68066  (void)pResourceManager;
68067 
68068  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
68069  {
68070  case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
68071  {
68072  ma_decoder_uninit(&pDataBuffer->connector.decoder);
68073  } break;
68074 
68075  case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
68076  {
68077  ma_audio_buffer_uninit(&pDataBuffer->connector.buffer);
68078  } break;
68079 
68080  case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
68081  {
68082  ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer);
68083  } break;
68084 
68085  case ma_resource_manager_data_supply_type_unknown:
68086  default:
68087  {
68088  /* Unknown data supply type. Should never happen. Need to post an error here. */
68089  return MA_INVALID_ARGS;
68090  };
68091  }
68092 
68093  return MA_SUCCESS;
68094 }
68095 
68096 static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode)
68097 {
68098  MA_ASSERT(pDataBufferNode != NULL);
68099  return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1);
68100 }
68101 
68102 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)
68103 {
68104  ma_result result;
68105  size_t dataSizeInBytes;
68106  void* pData;
68107 
68108  MA_ASSERT(pResourceManager != NULL);
68109  MA_ASSERT(pDataBufferNode != NULL);
68110  MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
68111 
68112  result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
68113  if (result != MA_SUCCESS) {
68114  if (pFilePath != NULL) {
68115  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));
68116  } else {
68117  #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
68118  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));
68119  #endif
68120  }
68121 
68122  return result;
68123  }
68124 
68125  pDataBufferNode->data.backend.encoded.pData = pData;
68126  pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes;
68127  ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */
68128 
68129  return MA_SUCCESS;
68130 }
68131 
68132 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)
68133 {
68134  ma_result result = MA_SUCCESS;
68135  ma_decoder* pDecoder;
68136  ma_uint64 totalFrameCount;
68137 
68138  MA_ASSERT(pResourceManager != NULL);
68139  MA_ASSERT(pDataBufferNode != NULL);
68140  MA_ASSERT(ppDecoder != NULL);
68141  MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
68142 
68143  *ppDecoder = NULL; /* For safety. */
68144 
68145  pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks);
68146  if (pDecoder == NULL) {
68147  return MA_OUT_OF_MEMORY;
68148  }
68149 
68150  result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder);
68151  if (result != MA_SUCCESS) {
68152  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
68153  return result;
68154  }
68155 
68156  /*
68157  At this point we have the decoder and we now need to initialize the data supply. This will
68158  be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap
68159  allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter
68160  is used when the length of a sound is unknown until a full decode has been performed.
68161  */
68162  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
68163  result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
68164  if (result != MA_SUCCESS) {
68165  return result;
68166  }
68167  } else {
68168  totalFrameCount = 0;
68169  }
68170 
68171  if (totalFrameCount > 0) {
68172  /* It's a known length. The data supply is a regular decoded buffer. */
68173  ma_uint64 dataSizeInBytes;
68174  void* pData;
68175 
68176  dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
68177  if (dataSizeInBytes > MA_SIZE_MAX) {
68178  ma_decoder_uninit(pDecoder);
68179  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
68180  return MA_TOO_BIG;
68181  }
68182 
68183  pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
68184  if (pData == NULL) {
68185  ma_decoder_uninit(pDecoder);
68186  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
68187  return MA_OUT_OF_MEMORY;
68188  }
68189 
68190  /* The buffer needs to be initialized to silence in case the caller reads from it. */
68191  ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels);
68192 
68193  /* Data has been allocated and the data supply can now be initialized. */
68194  pDataBufferNode->data.backend.decoded.pData = pData;
68195  pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount;
68196  pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat;
68197  pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels;
68198  pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate;
68199  pDataBufferNode->data.backend.decoded.decodedFrameCount = 0;
68200  ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */
68201  } else {
68202  /*
68203  It's an unknown length. The data supply is a paged decoded buffer. Setting this up is
68204  actually easier than the non-paged decoded buffer because we just need to initialize
68205  a ma_paged_audio_buffer object.
68206  */
68207  result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data);
68208  if (result != MA_SUCCESS) {
68209  ma_decoder_uninit(pDecoder);
68210  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
68211  return result;
68212  }
68213 
68214  pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate;
68215  pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0;
68216  ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */
68217  }
68218 
68219  *ppDecoder = pDecoder;
68220 
68221  return MA_SUCCESS;
68222 }
68223 
68224 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)
68225 {
68226  ma_result result = MA_SUCCESS;
68227  ma_uint64 pageSizeInFrames;
68228  ma_uint64 framesToTryReading;
68229  ma_uint64 framesRead;
68230 
68231  MA_ASSERT(pResourceManager != NULL);
68232  MA_ASSERT(pDataBufferNode != NULL);
68233  MA_ASSERT(pDecoder != NULL);
68234 
68235  /* We need to know the size of a page in frames to know how many frames to decode. */
68236  pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000);
68237  framesToTryReading = pageSizeInFrames;
68238 
68239  /*
68240  Here is where we do the decoding of the next page. We'll run a slightly different path depending
68241  on whether or not we're using a flat or paged buffer because the allocation of the page differs
68242  between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged
68243  buffer, we need to allocate a new page and attach it to the linked list.
68244  */
68245  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode))
68246  {
68247  case ma_resource_manager_data_supply_type_decoded:
68248  {
68249  /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */
68250  void* pDst;
68251  ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount;
68252  if (framesToTryReading > framesRemaining) {
68253  framesToTryReading = framesRemaining;
68254  }
68255 
68256  if (framesToTryReading > 0) {
68257  pDst = ma_offset_ptr(
68258  pDataBufferNode->data.backend.decoded.pData,
68259  pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels)
68260  );
68261  MA_ASSERT(pDst != NULL);
68262 
68263  result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead);
68264  if (framesRead > 0) {
68265  pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead;
68266  }
68267  } else {
68268  framesRead = 0;
68269  }
68270  } break;
68271 
68272  case ma_resource_manager_data_supply_type_decoded_paged:
68273  {
68274  /* The destination buffer is a freshly allocated page. */
68276 
68277  result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage);
68278  if (result != MA_SUCCESS) {
68279  return result;
68280  }
68281 
68282  result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead);
68283  if (framesRead > 0) {
68284  pPage->sizeInFrames = framesRead;
68285 
68286  result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage);
68287  if (result == MA_SUCCESS) {
68288  pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead;
68289  } else {
68290  /* Failed to append the page. Just abort and set the status to MA_AT_END. */
68291  ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
68292  result = MA_AT_END;
68293  }
68294  } else {
68295  /* No frames were read. Free the page and just set the status to MA_AT_END. */
68296  ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
68297  result = MA_AT_END;
68298  }
68299  } break;
68300 
68301  case ma_resource_manager_data_supply_type_encoded:
68302  case ma_resource_manager_data_supply_type_unknown:
68303  default:
68304  {
68305  /* Unexpected data supply type. */
68306  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));
68307  return MA_ERROR;
68308  };
68309  }
68310 
68311  if (result == MA_SUCCESS && framesRead == 0) {
68312  result = MA_AT_END;
68313  }
68314 
68315  return result;
68316 }
68317 
68318 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)
68319 {
68320  ma_result result = MA_SUCCESS;
68321  ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
68323 
68324  if (ppDataBufferNode != NULL) {
68325  *ppDataBufferNode = NULL;
68326  }
68327 
68328  result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint);
68329  if (result == MA_ALREADY_EXISTS) {
68330  /* The node already exists. We just need to increment the reference count. */
68331  pDataBufferNode = pInsertPoint;
68332 
68333  result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL);
68334  if (result != MA_SUCCESS) {
68335  return result; /* Should never happen. Failed to increment the reference count. */
68336  }
68337 
68338  result = MA_ALREADY_EXISTS;
68339  goto done;
68340  } else {
68341  /*
68342  The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This
68343  needs to be done inside the critical section to ensure an uninitialization of the node
68344  does not occur before initialization on another thread.
68345  */
68346  pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks);
68347  if (pDataBufferNode == NULL) {
68348  return MA_OUT_OF_MEMORY;
68349  }
68350 
68351  MA_ZERO_OBJECT(pDataBufferNode);
68352  pDataBufferNode->hashedName32 = hashedName32;
68353  pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */
68354 
68355  if (pExistingData == NULL) {
68356  pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */
68357  pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */
68358  pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE;
68359  } else {
68360  pDataBufferNode->data = *pExistingData;
68361  pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */
68362  pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE;
68363  }
68364 
68365  result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
68366  if (result != MA_SUCCESS) {
68367  ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
68368  return result; /* Should never happen. Failed to insert the data buffer into the BST. */
68369  }
68370 
68371  /*
68372  Here is where we'll post the job, but only if we're loading asynchronously. If we're
68373  loading synchronously we'll defer loading to a later stage, outside of the critical
68374  section.
68375  */
68376  if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
68377  /* Loading asynchronously. Post the job. */
68378  ma_job job;
68379  char* pFilePathCopy = NULL;
68380  wchar_t* pFilePathWCopy = NULL;
68381 
68382  /* 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. */
68383  if (pFilePath != NULL) {
68384  pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks);
68385  } else {
68386  pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks);
68387  }
68388 
68389  if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
68390  ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
68391  ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
68392  return MA_OUT_OF_MEMORY;
68393  }
68394 
68395  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68396  ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);
68397  }
68398 
68399  /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */
68400  if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }
68401  if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }
68402 
68403  /* We now have everything we need to post the job to the job thread. */
68404  job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE);
68405  job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
68406  job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager;
68407  job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode;
68408  job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy;
68409  job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy;
68410  job.data.resourceManager.loadDataBufferNode.flags = flags;
68411  job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL;
68412  job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL;
68413  job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence;
68414  job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence;
68415 
68416  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68417  result = ma_job_process(&job);
68418  } else {
68419  result = ma_resource_manager_post_job(pResourceManager, &job);
68420  }
68421 
68422  if (result != MA_SUCCESS) {
68423  /* Failed to post job. Probably ran out of memory. */
68424  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));
68425 
68426  /*
68427  Fences were acquired before posting the job, but since the job was not able to
68428  be posted, we need to make sure we release them so nothing gets stuck waiting.
68429  */
68430  if (pInitFence != NULL) { ma_fence_release(pInitFence); }
68431  if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }
68432 
68433  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68434  ma_resource_manager_inline_notification_uninit(pInitNotification);
68435  } else {
68436  /* 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. */
68437  ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
68438  ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
68439  }
68440 
68441  ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
68442  ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
68443 
68444  return result;
68445  }
68446  }
68447  }
68448 
68449 done:
68450  if (ppDataBufferNode != NULL) {
68451  *ppDataBufferNode = pDataBufferNode;
68452  }
68453 
68454  return result;
68455 }
68456 
68457 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)
68458 {
68459  ma_result result = MA_SUCCESS;
68460  ma_bool32 nodeAlreadyExists = MA_FALSE;
68461  ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
68462  ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
68463 
68464  if (ppDataBufferNode != NULL) {
68465  *ppDataBufferNode = NULL; /* Safety. */
68466  }
68467 
68468  if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) {
68469  return MA_INVALID_ARGS;
68470  }
68471 
68472  /* If we're specifying existing data, it must be valid. */
68473  if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) {
68474  return MA_INVALID_ARGS;
68475  }
68476 
68477  /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */
68478  if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
68479  flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
68480  }
68481 
68482  if (hashedName32 == 0) {
68483  if (pFilePath != NULL) {
68484  hashedName32 = ma_hash_string_32(pFilePath);
68485  } else {
68486  hashedName32 = ma_hash_string_w_32(pFilePathW);
68487  }
68488  }
68489 
68490  /*
68491  Here is where we either increment the node's reference count or allocate a new one and add it
68492  to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is
68493  posted inside the critical section just in case the caller immediately uninitializes the node
68494  as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the
68495  node is not uninitialized before initialization.
68496  */
68497  ma_resource_manager_data_buffer_bst_lock(pResourceManager);
68498  {
68499  result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode);
68500  }
68501  ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
68502 
68503  if (result == MA_ALREADY_EXISTS) {
68504  nodeAlreadyExists = MA_TRUE;
68505  result = MA_SUCCESS;
68506  } else {
68507  if (result != MA_SUCCESS) {
68508  return result;
68509  }
68510  }
68511 
68512  /*
68513  If we're loading synchronously, we'll need to load everything now. When loading asynchronously,
68514  a job will have been posted inside the BST critical section so that an uninitialization can be
68515  allocated an appropriate execution order thereby preventing it from being uninitialized before
68516  the node is initialized by the decoding thread(s).
68517  */
68518  if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */
68519  if (pFilePath == NULL && pFilePathW == NULL) {
68520  /*
68521  If this path is hit, it means a buffer is being copied (i.e. initialized from only the
68522  hashed name), but that node has been freed in the meantime, probably from some other
68523  thread. This is an invalid operation.
68524  */
68525  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");
68526  result = MA_INVALID_OPERATION;
68527  goto done;
68528  }
68529 
68530  if (pDataBufferNode->isDataOwnedByResourceManager) {
68531  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) {
68532  /* Loading synchronously. Load the sound in it's entirety here. */
68533  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) {
68534  /* No decoding. This is the simple case - just store the file contents in memory. */
68535  result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW);
68536  if (result != MA_SUCCESS) {
68537  goto done;
68538  }
68539  } else {
68540  /* Decoding. We do this the same way as we do when loading asynchronously. */
68541  ma_decoder* pDecoder;
68542  result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder);
68543  if (result != MA_SUCCESS) {
68544  goto done;
68545  }
68546 
68547  /* We have the decoder, now decode page by page just like we do when loading asynchronously. */
68548  for (;;) {
68549  /* Decode next page. */
68550  result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder);
68551  if (result != MA_SUCCESS) {
68552  break; /* Will return MA_AT_END when the last page has been decoded. */
68553  }
68554  }
68555 
68556  /* Reaching the end needs to be considered successful. */
68557  if (result == MA_AT_END) {
68558  result = MA_SUCCESS;
68559  }
68560 
68561  /*
68562  At this point the data buffer is either fully decoded or some error occurred. Either
68563  way, the decoder is no longer necessary.
68564  */
68565  ma_decoder_uninit(pDecoder);
68566  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
68567  }
68568 
68569  /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */
68570  ma_atomic_exchange_i32(&pDataBufferNode->result, result);
68571  } else {
68572  /* Loading asynchronously. We may need to wait for initialization. */
68573  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68574  ma_resource_manager_inline_notification_wait(&initNotification);
68575  }
68576  }
68577  } else {
68578  /* The data is not managed by the resource manager so there's nothing else to do. */
68579  MA_ASSERT(pExistingData != NULL);
68580  }
68581  }
68582 
68583 done:
68584  /* If we failed to initialize the data buffer we need to free it. */
68585  if (result != MA_SUCCESS) {
68586  if (nodeAlreadyExists == MA_FALSE) {
68587  ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
68588  ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
68589  }
68590  }
68591 
68592  /*
68593  The init notification needs to be uninitialized. This will be used if the node does not already
68594  exist, and we've specified ASYNC | WAIT_INIT.
68595  */
68596  if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
68597  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68598  ma_resource_manager_inline_notification_uninit(&initNotification);
68599  }
68600  }
68601 
68602  if (ppDataBufferNode != NULL) {
68603  *ppDataBufferNode = pDataBufferNode;
68604  }
68605 
68606  return result;
68607 }
68608 
68609 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)
68610 {
68611  ma_result result = MA_SUCCESS;
68612  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. */
68613  ma_uint32 hashedName32 = 0;
68614 
68615  if (pResourceManager == NULL) {
68616  return MA_INVALID_ARGS;
68617  }
68618 
68619  if (pDataBufferNode == NULL) {
68620  if (pName == NULL && pNameW == NULL) {
68621  return MA_INVALID_ARGS;
68622  }
68623 
68624  if (pName != NULL) {
68625  hashedName32 = ma_hash_string_32(pName);
68626  } else {
68627  hashedName32 = ma_hash_string_w_32(pNameW);
68628  }
68629  }
68630 
68631  /*
68632  The first thing to do is decrement the reference counter of the node. Then, if the reference
68633  count is zero, we need to free the node. If the node is still in the process of loading, we'll
68634  need to post a job to the job queue to free the node. Otherwise we'll just do it here.
68635  */
68636  ma_resource_manager_data_buffer_bst_lock(pResourceManager);
68637  {
68638  /* Might need to find the node. Must be done inside the critical section. */
68639  if (pDataBufferNode == NULL) {
68640  result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode);
68641  if (result != MA_SUCCESS) {
68642  goto stage2; /* Couldn't find the node. */
68643  }
68644  }
68645 
68646  result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount);
68647  if (result != MA_SUCCESS) {
68648  goto stage2; /* Should never happen. */
68649  }
68650 
68651  if (refCount == 0) {
68652  result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
68653  if (result != MA_SUCCESS) {
68654  goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */
68655  }
68656  }
68657  }
68658  ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
68659 
68660 stage2:
68661  if (result != MA_SUCCESS) {
68662  return result;
68663  }
68664 
68665  /*
68666  Here is where we need to free the node. We don't want to do this inside the critical section
68667  above because we want to keep that as small as possible for multi-threaded efficiency.
68668  */
68669  if (refCount == 0) {
68670  if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
68671  /* The sound is still loading. We need to delay the freeing of the node to a safe time. */
68672  ma_job job;
68673 
68674  /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */
68675  ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE);
68676 
68677  job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE);
68678  job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
68679  job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager;
68680  job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode;
68681 
68682  result = ma_resource_manager_post_job(pResourceManager, &job);
68683  if (result != MA_SUCCESS) {
68684  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));
68685  return result;
68686  }
68687 
68688  /* If we don't support threading, process the job queue here. */
68689  if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
68690  while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
68691  result = ma_resource_manager_process_next_job(pResourceManager);
68692  if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
68693  result = MA_SUCCESS;
68694  break;
68695  }
68696  }
68697  } else {
68698  /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */
68699  }
68700  } else {
68701  /* The sound isn't loading so we can just free the node here. */
68702  ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
68703  }
68704  }
68705 
68706  return result;
68707 }
68708 
68709 
68710 
68711 static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer)
68712 {
68713  MA_ASSERT(pDataBuffer != NULL);
68714  return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1);
68715 }
68716 
68717 static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
68718 {
68719  return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
68720 }
68721 
68722 static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
68723 {
68724  return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex);
68725 }
68726 
68727 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)
68728 {
68729  return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
68730 }
68731 
68732 static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
68733 {
68734  return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor);
68735 }
68736 
68737 static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
68738 {
68739  return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength);
68740 }
68741 
68742 static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
68743 {
68745  MA_ASSERT(pDataBuffer != NULL);
68746 
68747  ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
68748 
68749  /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */
68750  ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);
68751 
68752  return MA_SUCCESS;
68753 }
68754 
68755 static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
68756 {
68757  ma_resource_manager_data_buffer_cb__read_pcm_frames,
68758  ma_resource_manager_data_buffer_cb__seek_to_pcm_frame,
68759  ma_resource_manager_data_buffer_cb__get_data_format,
68760  ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames,
68761  ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames,
68762  ma_resource_manager_data_buffer_cb__set_looping,
68763  0
68764 };
68765 
68766 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)
68767 {
68768  ma_result result = MA_SUCCESS;
68769  ma_resource_manager_data_buffer_node* pDataBufferNode;
68770  ma_data_source_config dataSourceConfig;
68771  ma_bool32 async;
68772  ma_uint32 flags;
68774 
68775  if (pDataBuffer == NULL) {
68776  if (pConfig != NULL && pConfig->pNotifications != NULL) {
68777  ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
68778  }
68779 
68780  return MA_INVALID_ARGS;
68781  }
68782 
68783  MA_ZERO_OBJECT(pDataBuffer);
68784 
68785  if (pConfig == NULL) {
68786  return MA_INVALID_ARGS;
68787  }
68788 
68789  if (pConfig->pNotifications != NULL) {
68790  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. */
68791  } else {
68792  MA_ZERO_OBJECT(&notifications);
68793  }
68794 
68795  /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */
68796  flags = pConfig->flags;
68797  if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
68798  flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
68799  }
68800 
68801  async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;
68802 
68803  /*
68804  Fences need to be acquired before doing anything. These must be acquired and released outside of
68805  the node to ensure there's no holes where ma_fence_wait() could prematurely return before the
68806  data buffer has completed initialization.
68807 
68808  When loading asynchronously, the node acquisition routine below will acquire the fences on this
68809  thread and then release them on the async thread when the operation is complete.
68810 
68811  These fences are always released at the "done" tag at the end of this function. They'll be
68812  acquired a second if loading asynchronously. This double acquisition system is just done to
68813  simplify code maintanence.
68814  */
68815  ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
68816  {
68817  /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */
68818  result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
68819  if (result != MA_SUCCESS) {
68820  ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
68821  goto done;
68822  }
68823 
68824  dataSourceConfig = ma_data_source_config_init();
68825  dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
68826 
68827  result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
68828  if (result != MA_SUCCESS) {
68829  ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
68830  ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
68831  goto done;
68832  }
68833 
68834  pDataBuffer->pResourceManager = pResourceManager;
68835  pDataBuffer->pNode = pDataBufferNode;
68836  pDataBuffer->flags = flags;
68837  pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */
68838 
68839  /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */
68840  if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
68841  /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
68842  result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL);
68843  ma_atomic_exchange_i32(&pDataBuffer->result, result);
68844 
68845  ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
68846  goto done;
68847  } else {
68848  /* 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. */
68849  ma_job job;
68850  ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
68851 
68852  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68853  ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
68854  }
68855 
68856  /*
68857  The status of the data buffer needs to be set to MA_BUSY before posting the job so that the
68858  worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other
68859  than MA_BUSY, it'll assume an error and fall through to an early exit.
68860  */
68861  ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
68862 
68863  /* Acquire fences a second time. These will be released by the async thread. */
68864  ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
68865 
68866  job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER);
68867  job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
68868  job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer;
68869  job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
68870  job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification;
68871  job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence;
68872  job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence;
68873  job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
68874  job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
68875  job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
68876  job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
68877  job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping;
68878 
68879  /* If we need to wait for initialization to complete we can just process the job in place. */
68880  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68881  result = ma_job_process(&job);
68882  } else {
68883  result = ma_resource_manager_post_job(pResourceManager, &job);
68884  }
68885 
68886  if (result != MA_SUCCESS) {
68887  /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */
68888  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));
68889  ma_atomic_exchange_i32(&pDataBuffer->result, result);
68890 
68891  /* Release the fences after the result has been set on the data buffer. */
68892  ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
68893  } else {
68894  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68895  ma_resource_manager_inline_notification_wait(&initNotification);
68896 
68897  if (notifications.init.pNotification != NULL) {
68898  ma_async_notification_signal(notifications.init.pNotification);
68899  }
68900 
68901  /* NOTE: Do not release the init fence here. It will have been done by the job. */
68902 
68903  /* Make sure we return an error if initialization failed on the async thread. */
68904  result = ma_resource_manager_data_buffer_result(pDataBuffer);
68905  if (result == MA_BUSY) {
68906  result = MA_SUCCESS;
68907  }
68908  }
68909  }
68910 
68911  if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68912  ma_resource_manager_inline_notification_uninit(&initNotification);
68913  }
68914  }
68915 
68916  if (result != MA_SUCCESS) {
68917  ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
68918  goto done;
68919  }
68920  }
68921 done:
68922  if (result == MA_SUCCESS) {
68923  if (pConfig->initialSeekPointInPCMFrames > 0) {
68924  ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames);
68925  }
68926  }
68927 
68928  ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
68929 
68930  return result;
68931 }
68932 
68933 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)
68934 {
68935  return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer);
68936 }
68937 
68938 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)
68939 {
68941 
68942  config = ma_resource_manager_data_source_config_init();
68943  config.pFilePath = pFilePath;
68944  config.flags = flags;
68945  config.pNotifications = pNotifications;
68946 
68947  return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
68948 }
68949 
68950 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)
68951 {
68953 
68954  config = ma_resource_manager_data_source_config_init();
68955  config.pFilePathW = 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_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer)
68963 {
68965 
68966  if (pExistingDataBuffer == NULL) {
68967  return MA_INVALID_ARGS;
68968  }
68969 
68970  MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */
68971 
68972  config = ma_resource_manager_data_source_config_init();
68973  config.flags = pExistingDataBuffer->flags;
68974 
68975  return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer);
68976 }
68977 
68978 static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer)
68979 {
68980  MA_ASSERT(pDataBuffer != NULL);
68981 
68982  /* The connector should be uninitialized first. */
68983  ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer);
68984 
68985  /* With the connector uninitialized we can unacquire the node. */
68986  ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL);
68987 
68988  /* The base data source needs to be uninitialized as well. */
68989  ma_data_source_uninit(&pDataBuffer->ds);
68990 
68991  return MA_SUCCESS;
68992 }
68993 
68994 MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer)
68995 {
68996  ma_result result;
68997 
68998  if (pDataBuffer == NULL) {
68999  return MA_INVALID_ARGS;
69000  }
69001 
69002  if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) {
69003  /* The data buffer can be deleted synchronously. */
69004  return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
69005  } else {
69006  /*
69007  The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will
69008  be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event
69009  to get processed before returning.
69010  */
69011  ma_resource_manager_inline_notification notification;
69012  ma_job job;
69013 
69014  /*
69015  We need to mark the node as unavailable so we don't try reading from it anymore, but also to
69016  let the loading thread know that it needs to abort it's loading procedure.
69017  */
69018  ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE);
69019 
69020  result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, &notification);
69021  if (result != MA_SUCCESS) {
69022  return result; /* Failed to create the notification. This should rarely, if ever, happen. */
69023  }
69024 
69025  job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER);
69026  job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
69027  job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer;
69028  job.data.resourceManager.freeDataBuffer.pDoneNotification = &notification;
69029  job.data.resourceManager.freeDataBuffer.pDoneFence = NULL;
69030 
69031  result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
69032  if (result != MA_SUCCESS) {
69033  ma_resource_manager_inline_notification_uninit(&notification);
69034  return result;
69035  }
69036 
69037  ma_resource_manager_inline_notification_wait_and_uninit(&notification);
69038  }
69039 
69040  return result;
69041 }
69042 
69043 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)
69044 {
69045  ma_result result = MA_SUCCESS;
69046  ma_uint64 framesRead = 0;
69047  ma_bool32 isDecodedBufferBusy = MA_FALSE;
69048 
69049  /* Safety. */
69050  if (pFramesRead != NULL) {
69051  *pFramesRead = 0;
69052  }
69053 
69054  if (frameCount == 0) {
69055  return MA_INVALID_ARGS;
69056  }
69057 
69058  /*
69059  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
69060  it's been uninitialized or is in the process of uninitializing.
69061  */
69062  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
69063 
69064  /* If the node is not initialized we need to abort with a busy code. */
69065  if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
69066  return MA_BUSY; /* Still loading. */
69067  }
69068 
69069  /*
69070  If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's
69071  a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If
69072  this happens, we need to keep the seek scheduled and return MA_BUSY.
69073  */
69074  if (pDataBuffer->seekToCursorOnNextRead) {
69075  pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
69076 
69077  result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);
69078  if (result != MA_SUCCESS) {
69079  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) {
69080  pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */
69081  return MA_BUSY;
69082  }
69083 
69084  return result;
69085  }
69086  }
69087 
69088  /*
69089  For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot
69090  exceed this amount. We'll read as much as we can, and then return MA_BUSY.
69091  */
69092  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) {
69093  ma_uint64 availableFrames;
69094 
69095  isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);
69096 
69097  if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {
69098  /* Don't try reading more than the available frame count. */
69099  if (frameCount > availableFrames) {
69100  frameCount = availableFrames;
69101 
69102  /*
69103  If there's no frames available we want to set the status to MA_AT_END. The logic below
69104  will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this
69105  is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count
69106  is 0 because that'll result in a situation where it's possible MA_AT_END won't get
69107  returned.
69108  */
69109  if (frameCount == 0) {
69110  result = MA_AT_END;
69111  }
69112  } else {
69113  isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */
69114  }
69115  }
69116  }
69117 
69118  /* Don't attempt to read anything if we've got no frames available. */
69119  if (frameCount > 0) {
69120  result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead);
69121  }
69122 
69123  /*
69124  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
69125  as at the end and terminate decoding.
69126  */
69127  if (result == MA_AT_END) {
69128  if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
69129  result = MA_BUSY;
69130  }
69131  }
69132 
69133  if (isDecodedBufferBusy) {
69134  result = MA_BUSY;
69135  }
69136 
69137  if (pFramesRead != NULL) {
69138  *pFramesRead = framesRead;
69139  }
69140 
69141  if (result == MA_SUCCESS && framesRead == 0) {
69142  result = MA_AT_END;
69143  }
69144 
69145  return result;
69146 }
69147 
69148 MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex)
69149 {
69150  ma_result result;
69151 
69152  /* We cannot be using the data source after it's been uninitialized. */
69153  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
69154 
69155  /* If we haven't yet got a connector we need to abort. */
69156  if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
69157  pDataBuffer->seekTargetInPCMFrames = frameIndex;
69158  pDataBuffer->seekToCursorOnNextRead = MA_TRUE;
69159  return MA_BUSY; /* Still loading. */
69160  }
69161 
69162  result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex);
69163  if (result != MA_SUCCESS) {
69164  return result;
69165  }
69166 
69167  pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */
69168  pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
69169 
69170  return MA_SUCCESS;
69171 }
69172 
69173 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)
69174 {
69175  /* We cannot be using the data source after it's been uninitialized. */
69176  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
69177 
69178  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
69179  {
69180  case ma_resource_manager_data_supply_type_encoded:
69181  {
69182  return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
69183  };
69184 
69185  case ma_resource_manager_data_supply_type_decoded:
69186  {
69187  *pFormat = pDataBuffer->pNode->data.backend.decoded.format;
69188  *pChannels = pDataBuffer->pNode->data.backend.decoded.channels;
69189  *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate;
69190  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels);
69191  return MA_SUCCESS;
69192  };
69193 
69194  case ma_resource_manager_data_supply_type_decoded_paged:
69195  {
69196  *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format;
69197  *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels;
69198  *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate;
69199  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels);
69200  return MA_SUCCESS;
69201  };
69202 
69203  case ma_resource_manager_data_supply_type_unknown:
69204  {
69205  return MA_BUSY; /* Still loading. */
69206  };
69207 
69208  default:
69209  {
69210  /* Unknown supply type. Should never hit this. */
69211  return MA_INVALID_ARGS;
69212  }
69213  }
69214 }
69215 
69216 MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor)
69217 {
69218  /* We cannot be using the data source after it's been uninitialized. */
69219  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
69220 
69221  if (pDataBuffer == NULL || pCursor == NULL) {
69222  return MA_INVALID_ARGS;
69223  }
69224 
69225  *pCursor = 0;
69226 
69227  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
69228  {
69229  case ma_resource_manager_data_supply_type_encoded:
69230  {
69231  return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor);
69232  };
69233 
69234  case ma_resource_manager_data_supply_type_decoded:
69235  {
69236  return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor);
69237  };
69238 
69239  case ma_resource_manager_data_supply_type_decoded_paged:
69240  {
69241  return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor);
69242  };
69243 
69244  case ma_resource_manager_data_supply_type_unknown:
69245  {
69246  return MA_BUSY;
69247  };
69248 
69249  default:
69250  {
69251  return MA_INVALID_ARGS;
69252  }
69253  }
69254 }
69255 
69256 MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength)
69257 {
69258  /* We cannot be using the data source after it's been uninitialized. */
69259  MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
69260 
69261  if (pDataBuffer == NULL || pLength == NULL) {
69262  return MA_INVALID_ARGS;
69263  }
69264 
69265  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
69266  return MA_BUSY; /* Still loading. */
69267  }
69268 
69269  return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength);
69270 }
69271 
69272 MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer)
69273 {
69274  if (pDataBuffer == NULL) {
69275  return MA_INVALID_ARGS;
69276  }
69277 
69278  return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */
69279 }
69280 
69281 MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping)
69282 {
69283  return ma_data_source_set_looping(pDataBuffer, isLooping);
69284 }
69285 
69286 MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer)
69287 {
69288  return ma_data_source_is_looping(pDataBuffer);
69289 }
69290 
69291 MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames)
69292 {
69293  if (pAvailableFrames == NULL) {
69294  return MA_INVALID_ARGS;
69295  }
69296 
69297  *pAvailableFrames = 0;
69298 
69299  if (pDataBuffer == NULL) {
69300  return MA_INVALID_ARGS;
69301  }
69302 
69303  if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
69304  if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
69305  return MA_BUSY;
69306  } else {
69307  return MA_INVALID_OPERATION; /* No connector. */
69308  }
69309  }
69310 
69311  switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
69312  {
69313  case ma_resource_manager_data_supply_type_encoded:
69314  {
69315  return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames);
69316  };
69317 
69318  case ma_resource_manager_data_supply_type_decoded:
69319  {
69320  return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames);
69321  };
69322 
69323  case ma_resource_manager_data_supply_type_decoded_paged:
69324  {
69325  ma_uint64 cursor;
69326  ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor);
69327 
69328  if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) {
69329  *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor;
69330  } else {
69331  *pAvailableFrames = 0;
69332  }
69333 
69334  return MA_SUCCESS;
69335  };
69336 
69337  case ma_resource_manager_data_supply_type_unknown:
69338  default:
69339  {
69340  /* Unknown supply type. Should never hit this. */
69341  return MA_INVALID_ARGS;
69342  }
69343  }
69344 }
69345 
69346 MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags)
69347 {
69348  return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL);
69349 }
69350 
69351 MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags)
69352 {
69353  return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL);
69354 }
69355 
69356 
69357 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)
69358 {
69359  return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL);
69360 }
69361 
69362 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)
69363 {
69365  data.type = ma_resource_manager_data_supply_type_decoded;
69366  data.backend.decoded.pData = pData;
69367  data.backend.decoded.totalFrameCount = frameCount;
69368  data.backend.decoded.format = format;
69369  data.backend.decoded.channels = channels;
69370  data.backend.decoded.sampleRate = sampleRate;
69371 
69372  return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
69373 }
69374 
69375 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)
69376 {
69377  return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate);
69378 }
69379 
69380 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)
69381 {
69382  return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate);
69383 }
69384 
69385 
69386 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)
69387 {
69389  data.type = ma_resource_manager_data_supply_type_encoded;
69390  data.backend.encoded.pData = pData;
69391  data.backend.encoded.sizeInBytes = sizeInBytes;
69392 
69393  return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
69394 }
69395 
69396 MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes)
69397 {
69398  return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes);
69399 }
69400 
69401 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)
69402 {
69403  return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes);
69404 }
69405 
69406 
69407 MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath)
69408 {
69409  return ma_resource_manager_unregister_data(pResourceManager, pFilePath);
69410 }
69411 
69412 MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath)
69413 {
69414  return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath);
69415 }
69416 
69417 MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName)
69418 {
69419  return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL);
69420 }
69421 
69422 MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName)
69423 {
69424  return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName);
69425 }
69426 
69427 
69428 static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream)
69429 {
69430  MA_ASSERT(pDataStream != NULL);
69431  return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1);
69432 }
69433 
69434 static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream)
69435 {
69436  MA_ASSERT(pDataStream != NULL);
69437  return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd);
69438 }
69439 
69440 static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream)
69441 {
69442  MA_ASSERT(pDataStream != NULL);
69443  return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter);
69444 }
69445 
69446 
69447 static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
69448 {
69449  return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead);
69450 }
69451 
69452 static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
69453 {
69454  return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex);
69455 }
69456 
69457 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)
69458 {
69459  return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
69460 }
69461 
69462 static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
69463 {
69464  return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor);
69465 }
69466 
69467 static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
69468 {
69469  return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength);
69470 }
69471 
69472 static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
69473 {
69475  MA_ASSERT(pDataStream != NULL);
69476 
69477  ma_atomic_exchange_32(&pDataStream->isLooping, isLooping);
69478 
69479  return MA_SUCCESS;
69480 }
69481 
69482 static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =
69483 {
69484  ma_resource_manager_data_stream_cb__read_pcm_frames,
69485  ma_resource_manager_data_stream_cb__seek_to_pcm_frame,
69486  ma_resource_manager_data_stream_cb__get_data_format,
69487  ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames,
69488  ma_resource_manager_data_stream_cb__get_length_in_pcm_frames,
69489  ma_resource_manager_data_stream_cb__set_looping,
69490  0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/
69491 };
69492 
69493 static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor)
69494 {
69495  /* Loop if possible. */
69496  if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) {
69497  absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames;
69498  }
69499 
69500  ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor);
69501 }
69502 
69503 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)
69504 {
69505  ma_result result;
69506  ma_data_source_config dataSourceConfig;
69507  char* pFilePathCopy = NULL;
69508  wchar_t* pFilePathWCopy = NULL;
69509  ma_job job;
69510  ma_bool32 waitBeforeReturning = MA_FALSE;
69511  ma_resource_manager_inline_notification waitNotification;
69513 
69514  if (pDataStream == NULL) {
69515  if (pConfig != NULL && pConfig->pNotifications != NULL) {
69516  ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
69517  }
69518 
69519  return MA_INVALID_ARGS;
69520  }
69521 
69522  MA_ZERO_OBJECT(pDataStream);
69523 
69524  if (pConfig == NULL) {
69525  return MA_INVALID_ARGS;
69526  }
69527 
69528  if (pConfig->pNotifications != NULL) {
69529  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. */
69530  } else {
69531  MA_ZERO_OBJECT(&notifications);
69532  }
69533 
69534  dataSourceConfig = ma_data_source_config_init();
69535  dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable;
69536 
69537  result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);
69538  if (result != MA_SUCCESS) {
69539  ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
69540  return result;
69541  }
69542 
69543  pDataStream->pResourceManager = pResourceManager;
69544  pDataStream->flags = pConfig->flags;
69545  pDataStream->result = MA_BUSY;
69546 
69547  ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
69548  ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
69549  ma_data_source_set_looping(pDataStream, pConfig->isLooping);
69550 
69551  if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) {
69552  ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
69553  return MA_INVALID_ARGS;
69554  }
69555 
69556  /* 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. */
69557 
69558  /* 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. */
69559  if (pConfig->pFilePath != NULL) {
69560  pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks);
69561  } else {
69562  pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks);
69563  }
69564 
69565  if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
69566  ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
69567  return MA_OUT_OF_MEMORY;
69568  }
69569 
69570  /*
69571  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
69572  can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same.
69573  */
69574  if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
69575  waitBeforeReturning = MA_TRUE;
69576  ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);
69577  }
69578 
69579  ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
69580 
69581  /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */
69582  ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames);
69583 
69584  /* 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. */
69585  job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM);
69586  job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
69587  job.data.resourceManager.loadDataStream.pDataStream = pDataStream;
69588  job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy;
69589  job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy;
69590  job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames;
69591  job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;
69592  job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence;
69593  result = ma_resource_manager_post_job(pResourceManager, &job);
69594  if (result != MA_SUCCESS) {
69595  ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
69596  ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
69597 
69598  if (waitBeforeReturning) {
69599  ma_resource_manager_inline_notification_uninit(&waitNotification);
69600  }
69601 
69602  ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
69603  ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
69604  return result;
69605  }
69606 
69607  /* Wait if needed. */
69608  if (waitBeforeReturning) {
69609  ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);
69610 
69611  if (notifications.init.pNotification != NULL) {
69612  ma_async_notification_signal(notifications.init.pNotification);
69613  }
69614 
69615  /*
69616  If there was an error during initialization make sure we return that result here. We don't want to do this
69617  if we're not waiting because it will most likely be in a busy state.
69618  */
69619  if (pDataStream->result != MA_SUCCESS) {
69620  return pDataStream->result;
69621  }
69622 
69623  /* NOTE: Do not release pInitFence here. That will be done by the job. */
69624  }
69625 
69626  return MA_SUCCESS;
69627 }
69628 
69629 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)
69630 {
69632 
69633  config = ma_resource_manager_data_source_config_init();
69634  config.pFilePath = pFilePath;
69635  config.flags = flags;
69636  config.pNotifications = pNotifications;
69637 
69638  return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
69639 }
69640 
69641 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)
69642 {
69644 
69645  config = ma_resource_manager_data_source_config_init();
69646  config.pFilePathW = 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_uninit(ma_resource_manager_data_stream* pDataStream)
69654 {
69655  ma_resource_manager_inline_notification freeEvent;
69656  ma_job job;
69657 
69658  if (pDataStream == NULL) {
69659  return MA_INVALID_ARGS;
69660  }
69661 
69662  /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */
69663  ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE);
69664 
69665  /*
69666  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
69667  to wait for it to complete before returning which means we need an event.
69668  */
69669  ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent);
69670 
69671  job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM);
69672  job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
69673  job.data.resourceManager.freeDataStream.pDataStream = pDataStream;
69674  job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent;
69675  job.data.resourceManager.freeDataStream.pDoneFence = NULL;
69676  ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
69677 
69678  /* We need to wait for the job to finish processing before we return. */
69679  ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent);
69680 
69681  return MA_SUCCESS;
69682 }
69683 
69684 
69685 static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream)
69686 {
69687  MA_ASSERT(pDataStream != NULL);
69688  MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
69689 
69690  return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000);
69691 }
69692 
69693 static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor)
69694 {
69695  MA_ASSERT(pDataStream != NULL);
69696  MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
69697  MA_ASSERT(pageIndex == 0 || pageIndex == 1);
69698 
69699  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));
69700 }
69701 
69702 static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex)
69703 {
69704  ma_result result = MA_SUCCESS;
69705  ma_uint64 pageSizeInFrames;
69706  ma_uint64 totalFramesReadForThisPage = 0;
69707  void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0);
69708 
69709  pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
69710 
69711  /* The decoder needs to inherit the stream's looping and range state. */
69712  {
69713  ma_uint64 rangeBeg;
69714  ma_uint64 rangeEnd;
69715  ma_uint64 loopPointBeg;
69716  ma_uint64 loopPointEnd;
69717 
69718  ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream));
69719 
69720  ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd);
69721  ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd);
69722 
69723  ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd);
69724  ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd);
69725  }
69726 
69727  /* Just read straight from the decoder. It will deal with ranges and looping for us. */
69728  result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage);
69729  if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) {
69730  ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE);
69731  }
69732 
69733  ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);
69734  ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);
69735 }
69736 
69737 static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)
69738 {
69739  ma_uint32 iPage;
69740 
69741  MA_ASSERT(pDataStream != NULL);
69742 
69743  for (iPage = 0; iPage < 2; iPage += 1) {
69744  ma_resource_manager_data_stream_fill_page(pDataStream, iPage);
69745  }
69746 }
69747 
69748 
69749 static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount)
69750 {
69751  ma_uint64 framesAvailable;
69752  ma_uint64 frameCount = 0;
69753 
69754  /* We cannot be using the data source after it's been uninitialized. */
69755  MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69756 
69757  if (pFrameCount != NULL) {
69758  frameCount = *pFrameCount;
69759  *pFrameCount = 0;
69760  }
69761  if (ppFramesOut != NULL) {
69762  *ppFramesOut = NULL;
69763  }
69764 
69765  if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
69766  return MA_INVALID_ARGS;
69767  }
69768 
69769  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
69770  return MA_INVALID_OPERATION;
69771  }
69772 
69773  /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
69774  if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
69775  return MA_BUSY;
69776  }
69777 
69778  /* If the page we're on is invalid it means we've caught up to the job thread. */
69779  if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {
69780  framesAvailable = 0;
69781  } else {
69782  /*
69783  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
69784  that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler.
69785  */
69786  ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);
69787  MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);
69788 
69789  framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;
69790  }
69791 
69792  /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */
69793  if (framesAvailable == 0) {
69794  if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {
69795  return MA_AT_END;
69796  } else {
69797  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. */
69798  }
69799  }
69800 
69801  MA_ASSERT(framesAvailable > 0);
69802 
69803  if (frameCount > framesAvailable) {
69804  frameCount = framesAvailable;
69805  }
69806 
69807  *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);
69808  *pFrameCount = frameCount;
69809 
69810  return MA_SUCCESS;
69811 }
69812 
69813 static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount)
69814 {
69815  ma_uint32 newRelativeCursor;
69816  ma_uint32 pageSizeInFrames;
69817  ma_job job;
69818 
69819  /* We cannot be using the data source after it's been uninitialized. */
69820  MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69821 
69822  if (pDataStream == NULL) {
69823  return MA_INVALID_ARGS;
69824  }
69825 
69826  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
69827  return MA_INVALID_OPERATION;
69828  }
69829 
69830  /* The frame count should always fit inside a 32-bit integer. */
69831  if (frameCount > 0xFFFFFFFF) {
69832  return MA_INVALID_ARGS;
69833  }
69834 
69835  pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
69836 
69837  /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */
69838  ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount);
69839 
69840  /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */
69841  newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;
69842 
69843  /* 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. */
69844  if (newRelativeCursor >= pageSizeInFrames) {
69845  newRelativeCursor -= pageSizeInFrames;
69846 
69847  /* Here is where we post the job start decoding. */
69848  job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM);
69849  job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
69850  job.data.resourceManager.pageDataStream.pDataStream = pDataStream;
69851  job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex;
69852 
69853  /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */
69854  ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);
69855 
69856  /* Before posting the job we need to make sure we set some state. */
69857  pDataStream->relativeCursor = newRelativeCursor;
69858  pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;
69859  return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
69860  } else {
69861  /* We haven't moved into a new page so we can just move the cursor forward. */
69862  pDataStream->relativeCursor = newRelativeCursor;
69863  return MA_SUCCESS;
69864  }
69865 }
69866 
69867 
69868 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)
69869 {
69870  ma_result result = MA_SUCCESS;
69871  ma_uint64 totalFramesProcessed;
69872  ma_format format;
69873  ma_uint32 channels;
69874 
69875  /* Safety. */
69876  if (pFramesRead != NULL) {
69877  *pFramesRead = 0;
69878  }
69879 
69880  if (frameCount == 0) {
69881  return MA_INVALID_ARGS;
69882  }
69883 
69884  /* We cannot be using the data source after it's been uninitialized. */
69885  MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69886 
69887  if (pDataStream == NULL) {
69888  return MA_INVALID_ARGS;
69889  }
69890 
69891  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
69892  return MA_INVALID_OPERATION;
69893  }
69894 
69895  /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
69896  if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
69897  return MA_BUSY;
69898  }
69899 
69900  ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0);
69901 
69902  /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */
69903  totalFramesProcessed = 0;
69904  while (totalFramesProcessed < frameCount) {
69905  void* pMappedFrames;
69906  ma_uint64 mappedFrameCount;
69907 
69908  mappedFrameCount = frameCount - totalFramesProcessed;
69909  result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount);
69910  if (result != MA_SUCCESS) {
69911  break;
69912  }
69913 
69914  /* 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. */
69915  if (pFramesOut != NULL) {
69916  ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels);
69917  }
69918 
69919  totalFramesProcessed += mappedFrameCount;
69920 
69921  result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount);
69922  if (result != MA_SUCCESS) {
69923  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. */
69924  }
69925  }
69926 
69927  if (pFramesRead != NULL) {
69928  *pFramesRead = totalFramesProcessed;
69929  }
69930 
69931  if (result == MA_SUCCESS && totalFramesProcessed == 0) {
69932  result = MA_AT_END;
69933  }
69934 
69935  return result;
69936 }
69937 
69938 MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex)
69939 {
69940  ma_job job;
69941  ma_result streamResult;
69942 
69943  streamResult = ma_resource_manager_data_stream_result(pDataStream);
69944 
69945  /* We cannot be using the data source after it's been uninitialized. */
69946  MA_ASSERT(streamResult != MA_UNAVAILABLE);
69947 
69948  if (pDataStream == NULL) {
69949  return MA_INVALID_ARGS;
69950  }
69951 
69952  if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {
69953  return MA_INVALID_OPERATION;
69954  }
69955 
69956  /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */
69957  if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) {
69958  if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) {
69959  return MA_SUCCESS;
69960  }
69961  }
69962 
69963 
69964  /* 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. */
69965  ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1);
69966 
69967  /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */
69968  ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex);
69969 
69970  /*
69971  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
69972  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
69973  the first page.
69974  */
69975  pDataStream->relativeCursor = 0;
69976  pDataStream->currentPageIndex = 0;
69977  ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE);
69978  ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE);
69979 
69980  /* 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. */
69981  ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE);
69982 
69983  /*
69984  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
69985  are invalid and any content contained within them will be discarded and replaced with newly decoded data.
69986  */
69987  job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM);
69988  job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
69989  job.data.resourceManager.seekDataStream.pDataStream = pDataStream;
69990  job.data.resourceManager.seekDataStream.frameIndex = frameIndex;
69991  return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
69992 }
69993 
69994 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)
69995 {
69996  /* We cannot be using the data source after it's been uninitialized. */
69997  MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69998 
69999  if (pFormat != NULL) {
70000  *pFormat = ma_format_unknown;
70001  }
70002 
70003  if (pChannels != NULL) {
70004  *pChannels = 0;
70005  }
70006 
70007  if (pSampleRate != NULL) {
70008  *pSampleRate = 0;
70009  }
70010 
70011  if (pChannelMap != NULL) {
70012  MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
70013  }
70014 
70015  if (pDataStream == NULL) {
70016  return MA_INVALID_ARGS;
70017  }
70018 
70019  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
70020  return MA_INVALID_OPERATION;
70021  }
70022 
70023  /*
70024  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
70025  such that the application is responsible for ensuring it's not called while uninitializing so it should be safe.
70026  */
70027  return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
70028 }
70029 
70030 MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor)
70031 {
70032  ma_result result;
70033 
70034  if (pCursor == NULL) {
70035  return MA_INVALID_ARGS;
70036  }
70037 
70038  *pCursor = 0;
70039 
70040  /* We cannot be using the data source after it's been uninitialized. */
70041  MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
70042 
70043  if (pDataStream == NULL) {
70044  return MA_INVALID_ARGS;
70045  }
70046 
70047  /*
70048  If the stream is in an erroneous state we need to return an invalid operation. We can allow
70049  this to be called when the data stream is in a busy state because the caller may have asked
70050  for an initial seek position and it's convenient to return that as the cursor position.
70051  */
70052  result = ma_resource_manager_data_stream_result(pDataStream);
70053  if (result != MA_SUCCESS && result != MA_BUSY) {
70054  return MA_INVALID_OPERATION;
70055  }
70056 
70057  *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor);
70058 
70059  return MA_SUCCESS;
70060 }
70061 
70062 MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength)
70063 {
70064  ma_result streamResult;
70065 
70066  if (pLength == NULL) {
70067  return MA_INVALID_ARGS;
70068  }
70069 
70070  *pLength = 0;
70071 
70072  streamResult = ma_resource_manager_data_stream_result(pDataStream);
70073 
70074  /* We cannot be using the data source after it's been uninitialized. */
70075  MA_ASSERT(streamResult != MA_UNAVAILABLE);
70076 
70077  if (pDataStream == NULL) {
70078  return MA_INVALID_ARGS;
70079  }
70080 
70081  if (streamResult != MA_SUCCESS) {
70082  return streamResult;
70083  }
70084 
70085  /*
70086  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
70087  calculated when we initialized it on the job thread.
70088  */
70089  *pLength = pDataStream->totalLengthInPCMFrames;
70090  if (*pLength == 0) {
70091  return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */
70092  }
70093 
70094  return MA_SUCCESS;
70095 }
70096 
70097 MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream)
70098 {
70099  if (pDataStream == NULL) {
70100  return MA_INVALID_ARGS;
70101  }
70102 
70103  return (ma_result)ma_atomic_load_i32(&pDataStream->result);
70104 }
70105 
70106 MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping)
70107 {
70108  return ma_data_source_set_looping(pDataStream, isLooping);
70109 }
70110 
70111 MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream)
70112 {
70113  if (pDataStream == NULL) {
70114  return MA_FALSE;
70115  }
70116 
70117  return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */
70118 }
70119 
70120 MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames)
70121 {
70122  ma_uint32 pageIndex0;
70123  ma_uint32 pageIndex1;
70124  ma_uint32 relativeCursor;
70125  ma_uint64 availableFrames;
70126 
70127  if (pAvailableFrames == NULL) {
70128  return MA_INVALID_ARGS;
70129  }
70130 
70131  *pAvailableFrames = 0;
70132 
70133  if (pDataStream == NULL) {
70134  return MA_INVALID_ARGS;
70135  }
70136 
70137  pageIndex0 = pDataStream->currentPageIndex;
70138  pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01;
70139  relativeCursor = pDataStream->relativeCursor;
70140 
70141  availableFrames = 0;
70142  if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) {
70143  availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor;
70144  if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) {
70145  availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]);
70146  }
70147  }
70148 
70149  *pAvailableFrames = availableFrames;
70150  return MA_SUCCESS;
70151 }
70152 
70153 
70154 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)
70155 {
70156  if (pDataSource == NULL) {
70157  return MA_INVALID_ARGS;
70158  }
70159 
70160  MA_ZERO_OBJECT(pDataSource);
70161 
70162  if (pConfig == NULL) {
70163  return MA_INVALID_ARGS;
70164  }
70165 
70166  if (pResourceManager == NULL) {
70167  return MA_INVALID_ARGS;
70168  }
70169 
70170  pDataSource->flags = pConfig->flags;
70171 
70172  return MA_SUCCESS;
70173 }
70174 
70175 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)
70176 {
70177  ma_result result;
70178 
70179  result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource);
70180  if (result != MA_SUCCESS) {
70181  return result;
70182  }
70183 
70184  /* The data source itself is just a data stream or a data buffer. */
70185  if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70186  return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream);
70187  } else {
70188  return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer);
70189  }
70190 }
70191 
70192 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)
70193 {
70195 
70196  config = ma_resource_manager_data_source_config_init();
70197  config.pFilePath = pName;
70198  config.flags = flags;
70199  config.pNotifications = pNotifications;
70200 
70201  return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
70202 }
70203 
70204 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)
70205 {
70207 
70208  config = ma_resource_manager_data_source_config_init();
70209  config.pFilePathW = 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_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource)
70217 {
70218  ma_result result;
70220 
70221  if (pExistingDataSource == NULL) {
70222  return MA_INVALID_ARGS;
70223  }
70224 
70225  config = ma_resource_manager_data_source_config_init();
70226  config.flags = pExistingDataSource->flags;
70227 
70228  result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource);
70229  if (result != MA_SUCCESS) {
70230  return result;
70231  }
70232 
70233  /* Copying can only be done from data buffers. Streams cannot be copied. */
70234  if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70235  return MA_INVALID_OPERATION;
70236  }
70237 
70238  return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer);
70239 }
70240 
70241 MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource)
70242 {
70243  if (pDataSource == NULL) {
70244  return MA_INVALID_ARGS;
70245  }
70246 
70247  /* All we need to is uninitialize the underlying data buffer or data stream. */
70248  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70249  return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream);
70250  } else {
70251  return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer);
70252  }
70253 }
70254 
70255 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)
70256 {
70257  /* Safety. */
70258  if (pFramesRead != NULL) {
70259  *pFramesRead = 0;
70260  }
70261 
70262  if (pDataSource == NULL) {
70263  return MA_INVALID_ARGS;
70264  }
70265 
70266  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70267  return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead);
70268  } else {
70269  return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead);
70270  }
70271 }
70272 
70273 MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex)
70274 {
70275  if (pDataSource == NULL) {
70276  return MA_INVALID_ARGS;
70277  }
70278 
70279  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70280  return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex);
70281  } else {
70282  return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex);
70283  }
70284 }
70285 
70286 MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
70287 {
70288  if (pDataSource == NULL) {
70289  return MA_INVALID_ARGS;
70290  }
70291 
70292  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70293  return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount);
70294  } else {
70295  return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
70296  }
70297 }
70298 
70299 MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount)
70300 {
70301  if (pDataSource == NULL) {
70302  return MA_INVALID_ARGS;
70303  }
70304 
70305  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70306  return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount);
70307  } else {
70308  return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
70309  }
70310 }
70311 
70312 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)
70313 {
70314  if (pDataSource == NULL) {
70315  return MA_INVALID_ARGS;
70316  }
70317 
70318  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70319  return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
70320  } else {
70321  return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
70322  }
70323 }
70324 
70325 MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor)
70326 {
70327  if (pDataSource == NULL) {
70328  return MA_INVALID_ARGS;
70329  }
70330 
70331  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70332  return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor);
70333  } else {
70334  return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor);
70335  }
70336 }
70337 
70338 MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength)
70339 {
70340  if (pDataSource == NULL) {
70341  return MA_INVALID_ARGS;
70342  }
70343 
70344  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70345  return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength);
70346  } else {
70347  return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength);
70348  }
70349 }
70350 
70351 MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource)
70352 {
70353  if (pDataSource == NULL) {
70354  return MA_INVALID_ARGS;
70355  }
70356 
70357  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70358  return ma_resource_manager_data_stream_result(&pDataSource->backend.stream);
70359  } else {
70360  return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer);
70361  }
70362 }
70363 
70364 MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping)
70365 {
70366  if (pDataSource == NULL) {
70367  return MA_INVALID_ARGS;
70368  }
70369 
70370  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70371  return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping);
70372  } else {
70373  return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping);
70374  }
70375 }
70376 
70377 MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource)
70378 {
70379  if (pDataSource == NULL) {
70380  return MA_FALSE;
70381  }
70382 
70383  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70384  return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream);
70385  } else {
70386  return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer);
70387  }
70388 }
70389 
70390 MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames)
70391 {
70392  if (pAvailableFrames == NULL) {
70393  return MA_INVALID_ARGS;
70394  }
70395 
70396  *pAvailableFrames = 0;
70397 
70398  if (pDataSource == NULL) {
70399  return MA_INVALID_ARGS;
70400  }
70401 
70402  if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
70403  return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames);
70404  } else {
70405  return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames);
70406  }
70407 }
70408 
70409 
70410 MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob)
70411 {
70412  if (pResourceManager == NULL) {
70413  return MA_INVALID_ARGS;
70414  }
70415 
70416  return ma_job_queue_post(&pResourceManager->jobQueue, pJob);
70417 }
70418 
70419 MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager)
70420 {
70421  ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);
70422  return ma_resource_manager_post_job(pResourceManager, &job);
70423 }
70424 
70425 MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob)
70426 {
70427  if (pResourceManager == NULL) {
70428  return MA_INVALID_ARGS;
70429  }
70430 
70431  return ma_job_queue_next(&pResourceManager->jobQueue, pJob);
70432 }
70433 
70434 
70435 static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob)
70436 {
70437  ma_result result = MA_SUCCESS;
70438  ma_resource_manager* pResourceManager;
70439  ma_resource_manager_data_buffer_node* pDataBufferNode;
70440 
70441  MA_ASSERT(pJob != NULL);
70442 
70443  pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager;
70444  MA_ASSERT(pResourceManager != NULL);
70445 
70446  pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode;
70447  MA_ASSERT(pDataBufferNode != NULL);
70448  MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */
70449 
70450  /* 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. */
70451  if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
70452  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. */
70453  }
70454 
70455  /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */
70456  if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) {
70457  result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */
70458  goto done;
70459  }
70460 
70461  /*
70462  We're ready to start loading. Essentially what we're doing here is initializing the data supply
70463  of the node. Once this is complete, data buffers can have their connectors initialized which
70464  will allow then to have audio data read from them.
70465 
70466  Note that when the data supply type has been moved away from "unknown", that is when other threads
70467  will determine that the node is available for data delivery and the data buffer connectors can be
70468  initialized. Therefore, it's important that it is set after the data supply has been initialized.
70469  */
70470  if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) {
70471  /*
70472  Decoding. This is the complex case because we're not going to be doing the entire decoding
70473  process here. Instead it's going to be split of multiple jobs and loaded in pages. The
70474  reason for this is to evenly distribute decoding time across multiple sounds, rather than
70475  having one huge sound hog all the available processing resources.
70476 
70477  The first thing we do is initialize a decoder. This is allocated on the heap and is passed
70478  around to the paging jobs. When the last paging job has completed it's processing, it'll
70479  free the decoder for us.
70480 
70481  This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job
70482  which is where the actual decoding work will be done. However, once this job is complete,
70483  the node will be in a state where data buffer connectors can be initialized.
70484  */
70485  ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */
70486  ma_job pageDataBufferNodeJob;
70487 
70488  /* Allocate the decoder by initializing a decoded data supply. */
70489  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);
70490 
70491  /*
70492  Don't ever propagate an MA_BUSY result code or else the resource manager will think the
70493  node is just busy decoding rather than in an error state. This should never happen, but
70494  including this logic for safety just in case.
70495  */
70496  if (result == MA_BUSY) {
70497  result = MA_ERROR;
70498  }
70499 
70500  if (result != MA_SUCCESS) {
70501  if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) {
70502  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));
70503  } else {
70504  #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
70505  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));
70506  #endif
70507  }
70508 
70509  goto done;
70510  }
70511 
70512  /*
70513  At this point the node's data supply is initialized and other threads can start initializing
70514  their data buffer connectors. However, no data will actually be available until we start to
70515  actually decode it. To do this, we need to post a paging job which is where the decoding
70516  work is done.
70517 
70518  Note that if an error occurred at an earlier point, this section will have been skipped.
70519  */
70520  pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE);
70521  pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
70522  pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager;
70523  pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode;
70524  pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder;
70525  pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification;
70526  pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence;
70527 
70528  /* The job has been set up so it can now be posted. */
70529  result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
70530 
70531  /*
70532  When we get here, we want to make sure the result code is set to MA_BUSY. The reason for
70533  this is that the result will be copied over to the node's internal result variable. In
70534  this case, since the decoding is still in-progress, we need to make sure the result code
70535  is set to MA_BUSY.
70536  */
70537  if (result != MA_SUCCESS) {
70538  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));
70539  ma_decoder_uninit(pDecoder);
70540  ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
70541  } else {
70542  result = MA_BUSY;
70543  }
70544  } else {
70545  /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */
70546  result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW);
70547  }
70548 
70549 
70550 done:
70551  /* File paths are no longer needed. */
70552  ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks);
70553  ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks);
70554 
70555  /*
70556  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
70557  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
70558  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
70559  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
70560  other error code would cause the buffer to look like it's in a state that it's not.
70561  */
70562  ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
70563 
70564  /* At this point initialization is complete and we can signal the notification if any. */
70565  if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) {
70566  ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification);
70567  }
70568  if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) {
70569  ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence);
70570  }
70571 
70572  /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */
70573  if (result != MA_BUSY) {
70574  if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) {
70575  ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification);
70576  }
70577  if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) {
70578  ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence);
70579  }
70580  }
70581 
70582  /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */
70583  ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
70584 
70585  /* A busy result should be considered successful from the point of view of the job system. */
70586  if (result == MA_BUSY) {
70587  result = MA_SUCCESS;
70588  }
70589 
70590  return result;
70591 }
70592 
70593 static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob)
70594 {
70595  ma_resource_manager* pResourceManager;
70596  ma_resource_manager_data_buffer_node* pDataBufferNode;
70597 
70598  MA_ASSERT(pJob != NULL);
70599 
70600  pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager;
70601  MA_ASSERT(pResourceManager != NULL);
70602 
70603  pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode;
70604  MA_ASSERT(pDataBufferNode != NULL);
70605 
70606  if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
70607  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70608  }
70609 
70610  ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
70611 
70612  /* The event needs to be signalled last. */
70613  if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) {
70614  ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification);
70615  }
70616 
70617  if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) {
70618  ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence);
70619  }
70620 
70621  ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
70622  return MA_SUCCESS;
70623 }
70624 
70625 static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob)
70626 {
70627  ma_result result = MA_SUCCESS;
70628  ma_resource_manager* pResourceManager;
70629  ma_resource_manager_data_buffer_node* pDataBufferNode;
70630 
70631  MA_ASSERT(pJob != NULL);
70632 
70633  pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager;
70634  MA_ASSERT(pResourceManager != NULL);
70635 
70636  pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode;
70637  MA_ASSERT(pDataBufferNode != NULL);
70638 
70639  if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
70640  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70641  }
70642 
70643  /* Don't do any more decoding if the data buffer has started the uninitialization process. */
70644  result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);
70645  if (result != MA_BUSY) {
70646  goto done;
70647  }
70648 
70649  /* We're ready to decode the next page. */
70650  result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
70651 
70652  /*
70653  If we have a success code by this point, we want to post another job. We're going to set the
70654  result back to MA_BUSY to make it clear that there's still more to load.
70655  */
70656  if (result == MA_SUCCESS) {
70657  ma_job newJob;
70658  newJob = *pJob; /* Everything is the same as the input job, except the execution order. */
70659  newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */
70660 
70661  result = ma_resource_manager_post_job(pResourceManager, &newJob);
70662 
70663  /* Since the sound isn't yet fully decoded we want the status to be set to busy. */
70664  if (result == MA_SUCCESS) {
70665  result = MA_BUSY;
70666  }
70667  }
70668 
70669 done:
70670  /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */
70671  if (result != MA_BUSY) {
70672  ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
70673  ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks);
70674  }
70675 
70676  /* If we reached the end we need to treat it as successful. */
70677  if (result == MA_AT_END) {
70678  result = MA_SUCCESS;
70679  }
70680 
70681  /* Make sure we set the result of node in case some error occurred. */
70682  ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
70683 
70684  /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */
70685  if (result != MA_BUSY) {
70686  if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) {
70687  ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification);
70688  }
70689 
70690  if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) {
70691  ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence);
70692  }
70693  }
70694 
70695  ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
70696  return result;
70697 }
70698 
70699 
70700 static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob)
70701 {
70702  ma_result result = MA_SUCCESS;
70703  ma_resource_manager* pResourceManager;
70704  ma_resource_manager_data_buffer* pDataBuffer;
70705  ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown;
70706  ma_bool32 isConnectorInitialized = MA_FALSE;
70707 
70708  /*
70709  All we're doing here is checking if the node has finished loading. If not, we just re-post the job
70710  and keep waiting. Otherwise we increment the execution counter and set the buffer's result code.
70711  */
70712  MA_ASSERT(pJob != NULL);
70713 
70714  pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer;
70715  MA_ASSERT(pDataBuffer != NULL);
70716 
70717  pResourceManager = pDataBuffer->pResourceManager;
70718 
70719  if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) {
70720  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. */
70721  }
70722 
70723  /*
70724  First thing we need to do is check whether or not the data buffer is getting deleted. If so we
70725  just abort, but making sure we increment the execution pointer.
70726  */
70727  result = ma_resource_manager_data_buffer_result(pDataBuffer);
70728  if (result != MA_BUSY) {
70729  goto done; /* <-- This will ensure the exucution pointer is incremented. */
70730  } else {
70731  result = MA_SUCCESS; /* <-- Make sure this is reset. */
70732  }
70733 
70734  /* Try initializing the connector if we haven't already. */
70735  isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer);
70736  if (isConnectorInitialized == MA_FALSE) {
70737  dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode);
70738 
70739  if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) {
70740  /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */
70741  ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */
70742  dataSourceConfig = ma_resource_manager_data_source_config_init();
70743  dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames;
70744  dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames;
70745  dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames;
70746  dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames;
70747  dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping;
70748 
70749  result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
70750  if (result != MA_SUCCESS) {
70751  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));
70752  goto done;
70753  }
70754  } else {
70755  /* 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. */
70756  }
70757  } else {
70758  /* The connector is already initialized. Nothing to do here. */
70759  }
70760 
70761  /*
70762  If the data node is still loading, we need to repost the job and *not* increment the execution
70763  pointer (i.e. we need to not fall through to the "done" label).
70764 
70765  There is a hole between here and the where the data connector is initialized where the data
70766  buffer node may have finished initializing. We need to check for this by checking the result of
70767  the data buffer node and whether or not we had an unknown data supply type at the time of
70768  trying to initialize the data connector.
70769  */
70770  result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
70771  if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) {
70772  return ma_resource_manager_post_job(pResourceManager, pJob);
70773  }
70774 
70775 done:
70776  /* Only move away from a busy code so that we don't trash any existing error codes. */
70777  ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result);
70778 
70779  /* Only signal the other threads after the result has been set just for cleanliness sake. */
70780  if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) {
70781  ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification);
70782  }
70783  if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) {
70784  ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence);
70785  }
70786 
70787  /*
70788  If at this point the data buffer has not had it's connector initialized, it means the
70789  notification event was never signalled which means we need to signal it here.
70790  */
70791  if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) {
70792  if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) {
70793  ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification);
70794  }
70795  if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) {
70796  ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence);
70797  }
70798  }
70799 
70800  ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
70801  return result;
70802 }
70803 
70804 static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)
70805 {
70806  ma_resource_manager* pResourceManager;
70807  ma_resource_manager_data_buffer* pDataBuffer;
70808 
70809  MA_ASSERT(pJob != NULL);
70810 
70811  pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer;
70812  MA_ASSERT(pDataBuffer != NULL);
70813 
70814  pResourceManager = pDataBuffer->pResourceManager;
70815 
70816  if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) {
70817  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70818  }
70819 
70820  ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
70821 
70822  /* The event needs to be signalled last. */
70823  if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) {
70824  ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification);
70825  }
70826 
70827  if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) {
70828  ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence);
70829  }
70830 
70831  ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
70832  return MA_SUCCESS;
70833 }
70834 
70835 static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)
70836 {
70837  ma_result result = MA_SUCCESS;
70838  ma_decoder_config decoderConfig;
70839  ma_uint32 pageBufferSizeInBytes;
70840  ma_resource_manager* pResourceManager;
70841  ma_resource_manager_data_stream* pDataStream;
70842 
70843  MA_ASSERT(pJob != NULL);
70844 
70845  pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream;
70846  MA_ASSERT(pDataStream != NULL);
70847 
70848  pResourceManager = pDataStream->pResourceManager;
70849 
70850  if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
70851  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70852  }
70853 
70854  if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) {
70855  result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */
70856  goto done;
70857  }
70858 
70859  /* We need to initialize the decoder first so we can determine the size of the pages. */
70860  decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager);
70861 
70862  if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) {
70863  result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder);
70864  } else {
70865  result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder);
70866  }
70867  if (result != MA_SUCCESS) {
70868  goto done;
70869  }
70870 
70871  /* Retrieve the total length of the file before marking the decoder as loaded. */
70872  if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
70873  result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);
70874  if (result != MA_SUCCESS) {
70875  goto done; /* Failed to retrieve the length. */
70876  }
70877  } else {
70878  pDataStream->totalLengthInPCMFrames = 0;
70879  }
70880 
70881  /*
70882  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
70883  and we don't want to have another thread trying to access the decoder while it's scanning.
70884  */
70885  pDataStream->isDecoderInitialized = MA_TRUE;
70886 
70887  /* We have the decoder so we can now initialize our page buffer. */
70888  pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels);
70889 
70890  pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks);
70891  if (pDataStream->pPageData == NULL) {
70892  ma_decoder_uninit(&pDataStream->decoder);
70893  result = MA_OUT_OF_MEMORY;
70894  goto done;
70895  }
70896 
70897  /* Seek to our initial seek point before filling the initial pages. */
70898  ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint);
70899 
70900  /* We have our decoder and our page buffer, so now we need to fill our pages. */
70901  ma_resource_manager_data_stream_fill_pages(pDataStream);
70902 
70903  /* And now we're done. We want to make sure the result is MA_SUCCESS. */
70904  result = MA_SUCCESS;
70905 
70906 done:
70907  ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks);
70908  ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks);
70909 
70910  /* 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). */
70911  ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result);
70912 
70913  /* Only signal the other threads after the result has been set just for cleanliness sake. */
70914  if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) {
70915  ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification);
70916  }
70917  if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) {
70918  ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence);
70919  }
70920 
70921  ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
70922  return result;
70923 }
70924 
70925 static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob)
70926 {
70927  ma_resource_manager* pResourceManager;
70928  ma_resource_manager_data_stream* pDataStream;
70929 
70930  MA_ASSERT(pJob != NULL);
70931 
70932  pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream;
70933  MA_ASSERT(pDataStream != NULL);
70934 
70935  pResourceManager = pDataStream->pResourceManager;
70936 
70937  if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
70938  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70939  }
70940 
70941  /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */
70942  MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE);
70943 
70944  if (pDataStream->isDecoderInitialized) {
70945  ma_decoder_uninit(&pDataStream->decoder);
70946  }
70947 
70948  if (pDataStream->pPageData != NULL) {
70949  ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks);
70950  pDataStream->pPageData = NULL; /* Just in case... */
70951  }
70952 
70953  ma_data_source_uninit(&pDataStream->ds);
70954 
70955  /* The event needs to be signalled last. */
70956  if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) {
70957  ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification);
70958  }
70959  if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) {
70960  ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence);
70961  }
70962 
70963  /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/
70964  return MA_SUCCESS;
70965 }
70966 
70967 static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob)
70968 {
70969  ma_result result = MA_SUCCESS;
70970  ma_resource_manager* pResourceManager;
70971  ma_resource_manager_data_stream* pDataStream;
70972 
70973  MA_ASSERT(pJob != NULL);
70974 
70975  pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream;
70976  MA_ASSERT(pDataStream != NULL);
70977 
70978  pResourceManager = pDataStream->pResourceManager;
70979 
70980  if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
70981  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70982  }
70983 
70984  /* For streams, the status should be MA_SUCCESS. */
70985  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
70986  result = MA_INVALID_OPERATION;
70987  goto done;
70988  }
70989 
70990  ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex);
70991 
70992 done:
70993  ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
70994  return result;
70995 }
70996 
70997 static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob)
70998 {
70999  ma_result result = MA_SUCCESS;
71000  ma_resource_manager* pResourceManager;
71001  ma_resource_manager_data_stream* pDataStream;
71002 
71003  MA_ASSERT(pJob != NULL);
71004 
71005  pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream;
71006  MA_ASSERT(pDataStream != NULL);
71007 
71008  pResourceManager = pDataStream->pResourceManager;
71009 
71010  if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
71011  return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
71012  }
71013 
71014  /* For streams the status should be MA_SUCCESS for this to do anything. */
71015  if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) {
71016  result = MA_INVALID_OPERATION;
71017  goto done;
71018  }
71019 
71020  /*
71021  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
71022  instead of initializing the decoder, we seek to a frame.
71023  */
71024  ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex);
71025 
71026  /* After seeking we'll need to reload the pages. */
71027  ma_resource_manager_data_stream_fill_pages(pDataStream);
71028 
71029  /* We need to let the public API know that we're done seeking. */
71030  ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1);
71031 
71032 done:
71033  ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
71034  return result;
71035 }
71036 
71037 MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob)
71038 {
71039  if (pResourceManager == NULL || pJob == NULL) {
71040  return MA_INVALID_ARGS;
71041  }
71042 
71043  return ma_job_process(pJob);
71044 }
71045 
71046 MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager)
71047 {
71048  ma_result result;
71049  ma_job job;
71050 
71051  if (pResourceManager == NULL) {
71052  return MA_INVALID_ARGS;
71053  }
71054 
71055  /* This will return MA_CANCELLED if the next job is a quit job. */
71056  result = ma_resource_manager_next_job(pResourceManager, &job);
71057  if (result != MA_SUCCESS) {
71058  return result;
71059  }
71060 
71061  return ma_job_process(&job);
71062 }
71063 #else
71064 /* 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. */
71065 static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
71066 static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
71067 static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
71068 static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
71069 static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
71070 static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
71071 static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
71072 static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
71073 static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
71074 #endif /* MA_NO_RESOURCE_MANAGER */
71075 
71076 
71077 #ifndef MA_NO_NODE_GRAPH
71078 /* 10ms @ 48K = 480. Must never exceed 65535. */
71079 #ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS
71080 #define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480
71081 #endif
71082 
71083 
71084 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);
71085 
71086 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)
71087 {
71088  #ifndef MA_NO_GENERATION
71089  {
71090  ma_waveform_config waveformConfig;
71091  ma_waveform waveform;
71092 
71093  waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400);
71094  ma_waveform_init(&waveformConfig, &waveform);
71095  ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL);
71096  }
71097  #else
71098  {
71099  (void)pFramesOut;
71100  (void)frameCount;
71101  (void)format;
71102  (void)channels;
71103  (void)sampleRate;
71104  #if defined(MA_DEBUG_OUTPUT)
71105  {
71106  #if _MSC_VER
71107  #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.")
71108  #endif
71109  }
71110  #endif
71111  }
71112  #endif
71113 }
71114 
71115 
71116 
71117 MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)
71118 {
71119  ma_node_graph_config config;
71120 
71121  MA_ZERO_OBJECT(&config);
71122  config.channels = channels;
71123  config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
71124 
71125  return config;
71126 }
71127 
71128 
71129 static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading)
71130 {
71131  MA_ASSERT(pNodeGraph != NULL);
71132  ma_atomic_exchange_32(&pNodeGraph->isReading, isReading);
71133 }
71134 
71135 #if 0
71136 static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph)
71137 {
71138  MA_ASSERT(pNodeGraph != NULL);
71139  return ma_atomic_load_32(&pNodeGraph->isReading);
71140 }
71141 #endif
71142 
71143 
71144 static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
71145 {
71146  ma_node_graph* pNodeGraph = (ma_node_graph*)pNode;
71147  ma_uint64 framesRead;
71148 
71149  ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead);
71150 
71151  *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */
71152 
71153  (void)ppFramesIn;
71154  (void)pFrameCountIn;
71155 }
71156 
71157 static ma_node_vtable g_node_graph_node_vtable =
71158 {
71159  ma_node_graph_node_process_pcm_frames,
71160  NULL, /* onGetRequiredInputFrameCount */
71161  0, /* 0 input buses. */
71162  1, /* 1 output bus. */
71163  0 /* Flags. */
71164 };
71165 
71166 static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
71167 {
71168  MA_ASSERT(pNode != NULL);
71169  MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1);
71170  MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1);
71171 
71172  /* Input channel count needs to be the same as the output channel count. */
71173  MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0));
71174 
71175  /* We don't need to do anything here because it's a passthrough. */
71176  (void)pNode;
71177  (void)ppFramesIn;
71178  (void)pFrameCountIn;
71179  (void)ppFramesOut;
71180  (void)pFrameCountOut;
71181 
71182 #if 0
71183  /* The data has already been mixed. We just need to move it to the output buffer. */
71184  if (ppFramesIn != NULL) {
71185  ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0));
71186  }
71187 #endif
71188 }
71189 
71190 static ma_node_vtable g_node_graph_endpoint_vtable =
71191 {
71192  ma_node_graph_endpoint_process_pcm_frames,
71193  NULL, /* onGetRequiredInputFrameCount */
71194  1, /* 1 input bus. */
71195  1, /* 1 output bus. */
71196  MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */
71197 };
71198 
71199 MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph)
71200 {
71201  ma_result result;
71202  ma_node_config baseConfig;
71203  ma_node_config endpointConfig;
71204 
71205  if (pNodeGraph == NULL) {
71206  return MA_INVALID_ARGS;
71207  }
71208 
71209  MA_ZERO_OBJECT(pNodeGraph);
71210  pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames;
71211  if (pNodeGraph->nodeCacheCapInFrames == 0) {
71212  pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
71213  }
71214 
71215 
71216  /* Base node so we can use the node graph as a node into another graph. */
71217  baseConfig = ma_node_config_init();
71218  baseConfig.vtable = &g_node_graph_node_vtable;
71219  baseConfig.pOutputChannels = &pConfig->channels;
71220 
71221  result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base);
71222  if (result != MA_SUCCESS) {
71223  return result;
71224  }
71225 
71226 
71227  /* Endpoint. */
71228  endpointConfig = ma_node_config_init();
71229  endpointConfig.vtable = &g_node_graph_endpoint_vtable;
71230  endpointConfig.pInputChannels = &pConfig->channels;
71231  endpointConfig.pOutputChannels = &pConfig->channels;
71232 
71233  result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint);
71234  if (result != MA_SUCCESS) {
71235  ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
71236  return result;
71237  }
71238 
71239  return MA_SUCCESS;
71240 }
71241 
71242 MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks)
71243 {
71244  if (pNodeGraph == NULL) {
71245  return;
71246  }
71247 
71248  ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
71249 }
71250 
71251 MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph)
71252 {
71253  if (pNodeGraph == NULL) {
71254  return NULL;
71255  }
71256 
71257  return &pNodeGraph->endpoint;
71258 }
71259 
71260 MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
71261 {
71262  ma_result result = MA_SUCCESS;
71263  ma_uint64 totalFramesRead;
71264  ma_uint32 channels;
71265 
71266  if (pFramesRead != NULL) {
71267  *pFramesRead = 0; /* Safety. */
71268  }
71269 
71270  if (pNodeGraph == NULL) {
71271  return MA_INVALID_ARGS;
71272  }
71273 
71274  channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
71275 
71276 
71277  /* We'll be nice and try to do a full read of all frameCount frames. */
71278  totalFramesRead = 0;
71279  while (totalFramesRead < frameCount) {
71280  ma_uint32 framesJustRead;
71281  ma_uint64 framesToRead = frameCount - totalFramesRead;
71282 
71283  if (framesToRead > 0xFFFFFFFF) {
71284  framesToRead = 0xFFFFFFFF;
71285  }
71286 
71287  ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
71288  {
71289  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));
71290  }
71291  ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
71292 
71293  totalFramesRead += framesJustRead;
71294 
71295  if (result != MA_SUCCESS) {
71296  break;
71297  }
71298 
71299  /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */
71300  if (framesJustRead == 0) {
71301  break;
71302  }
71303  }
71304 
71305  /* 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. */
71306  if (totalFramesRead < frameCount) {
71307  ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels);
71308  }
71309 
71310  if (pFramesRead != NULL) {
71311  *pFramesRead = totalFramesRead;
71312  }
71313 
71314  return result;
71315 }
71316 
71317 MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph)
71318 {
71319  if (pNodeGraph == NULL) {
71320  return 0;
71321  }
71322 
71323  return ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
71324 }
71325 
71326 MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph)
71327 {
71328  if (pNodeGraph == NULL) {
71329  return 0;
71330  }
71331 
71332  return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */
71333 }
71334 
71335 MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime)
71336 {
71337  if (pNodeGraph == NULL) {
71338  return MA_INVALID_ARGS;
71339  }
71340 
71341  return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */
71342 }
71343 
71344 
71345 #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. */
71346 
71347 static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus)
71348 {
71349  MA_ASSERT(pOutputBus != NULL);
71350  MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT);
71351  MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode));
71352  MA_ASSERT(channels < 256);
71353 
71354  MA_ZERO_OBJECT(pOutputBus);
71355 
71356  if (channels == 0) {
71357  return MA_INVALID_ARGS;
71358  }
71359 
71360  pOutputBus->pNode = pNode;
71361  pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex;
71362  pOutputBus->channels = (ma_uint8)channels;
71363  pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */
71364  pOutputBus->volume = 1;
71365 
71366  return MA_SUCCESS;
71367 }
71368 
71369 static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus)
71370 {
71371  ma_spinlock_lock(&pOutputBus->lock);
71372 }
71373 
71374 static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus)
71375 {
71376  ma_spinlock_unlock(&pOutputBus->lock);
71377 }
71378 
71379 
71380 static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus)
71381 {
71382  return pOutputBus->channels;
71383 }
71384 
71385 
71386 static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead)
71387 {
71388  if (hasRead) {
71389  ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
71390  } else {
71391  ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
71392  }
71393 }
71394 
71395 static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus)
71396 {
71397  return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0;
71398 }
71399 
71400 
71401 static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached)
71402 {
71403  ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached);
71404 }
71405 
71406 static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus)
71407 {
71408  return ma_atomic_load_32(&pOutputBus->isAttached);
71409 }
71410 
71411 
71412 static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume)
71413 {
71414  MA_ASSERT(pOutputBus != NULL);
71415 
71416  if (volume < 0.0f) {
71417  volume = 0.0f;
71418  }
71419 
71420  ma_atomic_exchange_f32(&pOutputBus->volume, volume);
71421 
71422  return MA_SUCCESS;
71423 }
71424 
71425 static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus)
71426 {
71427  return ma_atomic_load_f32((float*)&pOutputBus->volume);
71428 }
71429 
71430 
71431 static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)
71432 {
71433  MA_ASSERT(pInputBus != NULL);
71434  MA_ASSERT(channels < 256);
71435 
71436  MA_ZERO_OBJECT(pInputBus);
71437 
71438  if (channels == 0) {
71439  return MA_INVALID_ARGS;
71440  }
71441 
71442  pInputBus->channels = (ma_uint8)channels;
71443 
71444  return MA_SUCCESS;
71445 }
71446 
71447 static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)
71448 {
71449  MA_ASSERT(pInputBus != NULL);
71450 
71451  ma_spinlock_lock(&pInputBus->lock);
71452 }
71453 
71454 static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)
71455 {
71456  MA_ASSERT(pInputBus != NULL);
71457 
71458  ma_spinlock_unlock(&pInputBus->lock);
71459 }
71460 
71461 
71462 static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus)
71463 {
71464  ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1);
71465 }
71466 
71467 static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus)
71468 {
71469  ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1);
71470 }
71471 
71472 static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus)
71473 {
71474  return ma_atomic_load_32(&pInputBus->nextCounter);
71475 }
71476 
71477 
71478 static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus)
71479 {
71480  return pInputBus->channels;
71481 }
71482 
71483 
71484 static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
71485 {
71486  MA_ASSERT(pInputBus != NULL);
71487  MA_ASSERT(pOutputBus != NULL);
71488 
71489  /*
71490  Mark the output bus as detached first. This will prevent future iterations on the audio thread
71491  from iterating this output bus.
71492  */
71493  ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE);
71494 
71495  /*
71496  We cannot use the output bus lock here since it'll be getting used at a higher level, but we do
71497  still need to use the input bus lock since we'll be updating pointers on two different output
71498  buses. The same rules apply here as the attaching case. Although we're using a lock here, we're
71499  *not* using a lock when iterating over the list in the audio thread. We therefore need to craft
71500  this in a way such that the iteration on the audio thread doesn't break.
71501 
71502  The the first thing to do is swap out the "next" pointer of the previous output bus with the
71503  new "next" output bus. This is the operation that matters for iteration on the audio thread.
71504  After that, the previous pointer on the new "next" pointer needs to be updated, after which
71505  point the linked list will be in a good state.
71506  */
71507  ma_node_input_bus_lock(pInputBus);
71508  {
71509  ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev);
71510  ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext);
71511 
71512  if (pOldPrev != NULL) {
71513  ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */
71514  }
71515  if (pOldNext != NULL) {
71516  ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */
71517  }
71518  }
71519  ma_node_input_bus_unlock(pInputBus);
71520 
71521  /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */
71522  ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */
71523  ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */
71524  pOutputBus->pInputNode = NULL;
71525  pOutputBus->inputNodeInputBusIndex = 0;
71526 
71527 
71528  /*
71529  For thread-safety reasons, we don't want to be returning from this straight away. We need to
71530  wait for the audio thread to finish with the output bus. There's two things we need to wait
71531  for. The first is the part that selects the next output bus in the list, and the other is the
71532  part that reads from the output bus. Basically all we're doing is waiting for the input bus
71533  to stop referencing the output bus.
71534 
71535  We're doing this part last because we want the section above to run while the audio thread
71536  is finishing up with the output bus, just for efficiency reasons. We marked the output bus as
71537  detached right at the top of this function which is going to prevent the audio thread from
71538  iterating the output bus again.
71539  */
71540 
71541  /* Part 1: Wait for the current iteration to complete. */
71542  while (ma_node_input_bus_get_next_counter(pInputBus) > 0) {
71543  ma_yield();
71544  }
71545 
71546  /* Part 2: Wait for any reads to complete. */
71547  while (ma_atomic_load_32(&pOutputBus->refCount) > 0) {
71548  ma_yield();
71549  }
71550 
71551  /*
71552  At this point we're done detaching and we can be guaranteed that the audio thread is not going
71553  to attempt to reference this output bus again (until attached again).
71554  */
71555 }
71556 
71557 #if 0 /* Not used at the moment, but leaving here in case I need it later. */
71558 static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
71559 {
71560  MA_ASSERT(pInputBus != NULL);
71561  MA_ASSERT(pOutputBus != NULL);
71562 
71563  ma_node_output_bus_lock(pOutputBus);
71564  {
71565  ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
71566  }
71567  ma_node_output_bus_unlock(pOutputBus);
71568 }
71569 #endif
71570 
71571 static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex)
71572 {
71573  MA_ASSERT(pInputBus != NULL);
71574  MA_ASSERT(pOutputBus != NULL);
71575 
71576  ma_node_output_bus_lock(pOutputBus);
71577  {
71578  ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode);
71579 
71580  /* Detach from any existing attachment first if necessary. */
71581  if (pOldInputNode != NULL) {
71582  ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
71583  }
71584 
71585  /*
71586  At this point we can be sure the output bus is not attached to anything. The linked list in the
71587  old input bus has been updated so that pOutputBus will not get iterated again.
71588  */
71589  pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */
71590  pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex;
71591 
71592  /*
71593  Now we need to attach the output bus to the linked list. This involves updating two pointers on
71594  two different output buses so I'm going to go ahead and keep this simple and just use a lock.
71595  There are ways to do this without a lock, but it's just too hard to maintain for it's value.
71596 
71597  Although we're locking here, it's important to remember that we're *not* locking when iterating
71598  and reading audio data since that'll be running on the audio thread. As a result we need to be
71599  careful how we craft this so that we don't break iteration. What we're going to do is always
71600  attach the new item so that it becomes the first item in the list. That way, as we're iterating
71601  we won't break any links in the list and iteration will continue safely. The detaching case will
71602  also be crafted in a way as to not break list iteration. It's important to remember to use
71603  atomic exchanges here since no locking is happening on the audio thread during iteration.
71604  */
71605  ma_node_input_bus_lock(pInputBus);
71606  {
71607  ma_node_output_bus* pNewPrev = &pInputBus->head;
71608  ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext);
71609 
71610  /* Update the local output bus. */
71611  ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev);
71612  ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext);
71613 
71614  /* Update the other output buses to point back to the local output bus. */
71615  ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */
71616 
71617  /* Do the previous pointer last. This is only used for detachment. */
71618  if (pNewNext != NULL) {
71619  ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus);
71620  }
71621  }
71622  ma_node_input_bus_unlock(pInputBus);
71623 
71624  /*
71625  Mark the node as attached last. This is used to controlling whether or the output bus will be
71626  iterated on the audio thread. Mainly required for detachment purposes.
71627  */
71628  ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE);
71629  }
71630  ma_node_output_bus_unlock(pOutputBus);
71631 }
71632 
71633 static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
71634 {
71635  ma_node_output_bus* pNext;
71636 
71637  MA_ASSERT(pInputBus != NULL);
71638 
71639  if (pOutputBus == NULL) {
71640  return NULL;
71641  }
71642 
71643  ma_node_input_bus_next_begin(pInputBus);
71644  {
71645  pNext = pOutputBus;
71646  for (;;) {
71647  pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext);
71648  if (pNext == NULL) {
71649  break; /* Reached the end. */
71650  }
71651 
71652  if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) {
71653  continue; /* The node is not attached. Keep checking. */
71654  }
71655 
71656  /* The next node has been selected. */
71657  break;
71658  }
71659 
71660  /* We need to increment the reference count of the selected node. */
71661  if (pNext != NULL) {
71662  ma_atomic_fetch_add_32(&pNext->refCount, 1);
71663  }
71664 
71665  /* The previous node is no longer being referenced. */
71666  ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1);
71667  }
71668  ma_node_input_bus_next_end(pInputBus);
71669 
71670  return pNext;
71671 }
71672 
71673 static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)
71674 {
71675  return ma_node_input_bus_next(pInputBus, &pInputBus->head);
71676 }
71677 
71678 
71679 
71680 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)
71681 {
71682  ma_result result = MA_SUCCESS;
71683  ma_node_output_bus* pOutputBus;
71684  ma_node_output_bus* pFirst;
71685  ma_uint32 inputChannels;
71686  ma_bool32 doesOutputBufferHaveContent = MA_FALSE;
71687 
71688  (void)pInputNode; /* Not currently used. */
71689 
71690  /*
71691  This will be called from the audio thread which means we can't be doing any locking. Basically,
71692  this function will not perfom any locking, whereas attaching and detaching will, but crafted in
71693  such a way that we don't need to perform any locking here. The important thing to remember is
71694  to always iterate in a forward direction.
71695 
71696  In order to process any data we need to first read from all input buses. That's where this
71697  function comes in. This iterates over each of the attachments and accumulates/mixes them. We
71698  also convert the channels to the nodes output channel count before mixing. We want to do this
71699  channel conversion so that the caller of this function can invoke the processing callback
71700  without having to do it themselves.
71701 
71702  When we iterate over each of the attachments on the input bus, we need to read as much data as
71703  we can from each of them so that we don't end up with holes between each of the attachments. To
71704  do this, we need to read from each attachment in a loop and read as many frames as we can, up
71705  to `frameCount`.
71706  */
71707  MA_ASSERT(pInputNode != NULL);
71708  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. */
71709 
71710  *pFramesRead = 0; /* Safety. */
71711 
71712  inputChannels = ma_node_input_bus_get_channels(pInputBus);
71713 
71714  /*
71715  We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They
71716  are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first()
71717  once per iteration, however we have an optimization to checks whether or not it's the first item in
71718  the list. We therefore need to store a pointer to the first item rather than repeatedly calling
71719  ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it
71720  after calling ma_node_input_bus_next(), which we won't be.
71721  */
71722  pFirst = ma_node_input_bus_first(pInputBus);
71723  if (pFirst == NULL) {
71724  return MA_SUCCESS; /* No attachments. Read nothing. */
71725  }
71726 
71727  for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {
71728  ma_uint32 framesProcessed = 0;
71729  ma_bool32 isSilentOutput = MA_FALSE;
71730 
71731  MA_ASSERT(pOutputBus->pNode != NULL);
71732  MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL);
71733 
71734  isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0;
71735 
71736  if (pFramesOut != NULL) {
71737  /* Read. */
71738  float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
71739  ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels;
71740 
71741  while (framesProcessed < frameCount) {
71742  float* pRunningFramesOut;
71743  ma_uint32 framesToRead;
71744  ma_uint32 framesJustRead;
71745 
71746  framesToRead = frameCount - framesProcessed;
71747  if (framesToRead > tempCapInFrames) {
71748  framesToRead = tempCapInFrames;
71749  }
71750 
71751  pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
71752 
71753  if (doesOutputBufferHaveContent == MA_FALSE) {
71754  /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */
71755  result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
71756  } else {
71757  /* Slow path. Not the first attachment. Mixing required. */
71758  result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed);
71759  if (result == MA_SUCCESS || result == MA_AT_END) {
71760  if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */
71761  ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1);
71762  }
71763  }
71764  }
71765 
71766  framesProcessed += framesJustRead;
71767 
71768  /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */
71769  if (result != MA_SUCCESS) {
71770  break;
71771  }
71772 
71773  /* If we didn't read anything, abort so we don't get stuck in a loop. */
71774  if (framesJustRead == 0) {
71775  break;
71776  }
71777  }
71778 
71779  /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */
71780  if (pOutputBus == pFirst && framesProcessed < frameCount) {
71781  ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels);
71782  }
71783 
71784  if (isSilentOutput == MA_FALSE) {
71785  doesOutputBufferHaveContent = MA_TRUE;
71786  }
71787  } else {
71788  /* Seek. */
71789  ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime);
71790  }
71791  }
71792 
71793  /* If we didn't output anything, output silence. */
71794  if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) {
71795  ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels);
71796  }
71797 
71798  /* In this path we always "process" the entire amount. */
71799  *pFramesRead = frameCount;
71800 
71801  return result;
71802 }
71803 
71804 
71805 MA_API ma_node_config ma_node_config_init(void)
71806 {
71807  ma_node_config config;
71808 
71809  MA_ZERO_OBJECT(&config);
71810  config.initialState = ma_node_state_started; /* Nodes are started by default. */
71811  config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;
71812  config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;
71813 
71814  return config;
71815 }
71816 
71817 
71818 
71819 static ma_result ma_node_detach_full(ma_node* pNode);
71820 
71821 static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex)
71822 {
71823  ma_node_base* pNodeBase = (ma_node_base*)pNode;
71824  ma_uint32 iInputBus;
71825  float* pBasePtr;
71826 
71827  MA_ASSERT(pNodeBase != NULL);
71828 
71829  /* Input data is stored at the front of the buffer. */
71830  pBasePtr = pNodeBase->pCachedData;
71831  for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) {
71832  pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
71833  }
71834 
71835  return pBasePtr;
71836 }
71837 
71838 static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex)
71839 {
71840  ma_node_base* pNodeBase = (ma_node_base*)pNode;
71841  ma_uint32 iInputBus;
71842  ma_uint32 iOutputBus;
71843  float* pBasePtr;
71844 
71845  MA_ASSERT(pNodeBase != NULL);
71846 
71847  /* Cached output data starts after the input data. */
71848  pBasePtr = pNodeBase->pCachedData;
71849  for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
71850  pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
71851  }
71852 
71853  for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) {
71854  pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]);
71855  }
71856 
71857  return pBasePtr;
71858 }
71859 
71860 
71861 typedef struct
71862 {
71863  size_t sizeInBytes;
71864  size_t inputBusOffset;
71865  size_t outputBusOffset;
71866  size_t cachedDataOffset;
71867  ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */
71868  ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */
71869 } ma_node_heap_layout;
71870 
71871 static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount)
71872 {
71873  ma_uint32 inputBusCount;
71874  ma_uint32 outputBusCount;
71875 
71876  MA_ASSERT(pConfig != NULL);
71877  MA_ASSERT(pInputBusCount != NULL);
71878  MA_ASSERT(pOutputBusCount != NULL);
71879 
71880  /* 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. */
71881  if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
71882  inputBusCount = pConfig->inputBusCount;
71883  } else {
71884  inputBusCount = pConfig->vtable->inputBusCount;
71885 
71886  if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) {
71887  return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
71888  }
71889  }
71890 
71891  if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
71892  outputBusCount = pConfig->outputBusCount;
71893  } else {
71894  outputBusCount = pConfig->vtable->outputBusCount;
71895 
71896  if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) {
71897  return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
71898  }
71899  }
71900 
71901  /* Bus counts must be within limits. */
71902  if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) {
71903  return MA_INVALID_ARGS;
71904  }
71905 
71906 
71907  /* We must have channel counts for each bus. */
71908  if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) {
71909  return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */
71910  }
71911 
71912 
71913  /* Some special rules for passthrough nodes. */
71914  if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
71915  if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) {
71916  return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */
71917  }
71918 
71919  if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) {
71920  return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */
71921  }
71922  }
71923 
71924 
71925  *pInputBusCount = inputBusCount;
71926  *pOutputBusCount = outputBusCount;
71927 
71928  return MA_SUCCESS;
71929 }
71930 
71931 static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout)
71932 {
71933  ma_result result;
71934  ma_uint32 inputBusCount;
71935  ma_uint32 outputBusCount;
71936 
71937  MA_ASSERT(pHeapLayout != NULL);
71938 
71939  MA_ZERO_OBJECT(pHeapLayout);
71940 
71941  if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {
71942  return MA_INVALID_ARGS;
71943  }
71944 
71945  result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount);
71946  if (result != MA_SUCCESS) {
71947  return result;
71948  }
71949 
71950  pHeapLayout->sizeInBytes = 0;
71951 
71952  /* Input buses. */
71953  if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
71954  pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes;
71955  pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount);
71956  } else {
71957  pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */
71958  }
71959 
71960  /* Output buses. */
71961  if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
71962  pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes;
71963  pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount);
71964  } else {
71965  pHeapLayout->outputBusOffset = MA_SIZE_MAX;
71966  }
71967 
71968  /*
71969  Cached audio data.
71970 
71971  We need to allocate memory for a caching both input and output data. We have an optimization
71972  where no caching is necessary for specific conditions:
71973 
71974  - The node has 0 inputs and 1 output.
71975 
71976  When a node meets the above conditions, no cache is allocated.
71977 
71978  The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by
71979  allocating too much, but at the same time we want it be large enough so that enough frames can
71980  be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For
71981  now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile
71982  time. It might also be worth investigating whether or not this can be configured at run time.
71983  */
71984  if (inputBusCount == 0 && outputBusCount == 1) {
71985  /* Fast path. No cache needed. */
71986  pHeapLayout->cachedDataOffset = MA_SIZE_MAX;
71987  } else {
71988  /* Slow path. Cache needed. */
71989  size_t cachedDataSizeInBytes = 0;
71990  ma_uint32 iBus;
71991 
71992  for (iBus = 0; iBus < inputBusCount; iBus += 1) {
71993  cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
71994  }
71995 
71996  for (iBus = 0; iBus < outputBusCount; iBus += 1) {
71997  cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
71998  }
71999 
72000  pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;
72001  pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes);
72002  }
72003 
72004 
72005  /*
72006  Not technically part of the heap, but we can output the input and output bus counts so we can
72007  avoid a redundant call to ma_node_translate_bus_counts().
72008  */
72009  pHeapLayout->inputBusCount = inputBusCount;
72010  pHeapLayout->outputBusCount = outputBusCount;
72011 
72012  /* Make sure allocation size is aligned. */
72013  pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
72014 
72015  return MA_SUCCESS;
72016 }
72017 
72018 MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes)
72019 {
72020  ma_result result;
72021  ma_node_heap_layout heapLayout;
72022 
72023  if (pHeapSizeInBytes == NULL) {
72024  return MA_INVALID_ARGS;
72025  }
72026 
72027  *pHeapSizeInBytes = 0;
72028 
72029  result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
72030  if (result != MA_SUCCESS) {
72031  return result;
72032  }
72033 
72034  *pHeapSizeInBytes = heapLayout.sizeInBytes;
72035 
72036  return MA_SUCCESS;
72037 }
72038 
72039 MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode)
72040 {
72041  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72042  ma_result result;
72043  ma_node_heap_layout heapLayout;
72044  ma_uint32 iInputBus;
72045  ma_uint32 iOutputBus;
72046 
72047  if (pNodeBase == NULL) {
72048  return MA_INVALID_ARGS;
72049  }
72050 
72051  MA_ZERO_OBJECT(pNodeBase);
72052 
72053  result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
72054  if (result != MA_SUCCESS) {
72055  return result;
72056  }
72057 
72058  pNodeBase->_pHeap = pHeap;
72059  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
72060 
72061  pNodeBase->pNodeGraph = pNodeGraph;
72062  pNodeBase->vtable = pConfig->vtable;
72063  pNodeBase->state = pConfig->initialState;
72064  pNodeBase->stateTimes[ma_node_state_started] = 0;
72065  pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */
72066  pNodeBase->inputBusCount = heapLayout.inputBusCount;
72067  pNodeBase->outputBusCount = heapLayout.outputBusCount;
72068 
72069  if (heapLayout.inputBusOffset != MA_SIZE_MAX) {
72070  pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
72071  } else {
72072  pNodeBase->pInputBuses = pNodeBase->_inputBuses;
72073  }
72074 
72075  if (heapLayout.outputBusOffset != MA_SIZE_MAX) {
72076  pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset);
72077  } else {
72078  pNodeBase->pOutputBuses = pNodeBase->_outputBuses;
72079  }
72080 
72081  if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {
72082  pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);
72083  pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames;
72084  } else {
72085  pNodeBase->pCachedData = NULL;
72086  }
72087 
72088 
72089 
72090  /* We need to run an initialization step for each input and output bus. */
72091  for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
72092  result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);
72093  if (result != MA_SUCCESS) {
72094  return result;
72095  }
72096  }
72097 
72098  for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
72099  result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]);
72100  if (result != MA_SUCCESS) {
72101  return result;
72102  }
72103  }
72104 
72105 
72106  /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */
72107  if (pNodeBase->pCachedData != NULL) {
72108  ma_uint32 iBus;
72109 
72110  #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. */
72111  /* For safety we'll go ahead and default the buffer to silence. */
72112  for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
72113  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]));
72114  }
72115  for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
72116  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]));
72117  }
72118  #else
72119  /* For debugging. Default to a sine wave. */
72120  for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
72121  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);
72122  }
72123  for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
72124  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);
72125  }
72126  #endif
72127  }
72128 
72129  return MA_SUCCESS;
72130 }
72131 
72132 MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)
72133 {
72134  ma_result result;
72135  size_t heapSizeInBytes;
72136  void* pHeap;
72137 
72138  result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes);
72139  if (result != MA_SUCCESS) {
72140  return result;
72141  }
72142 
72143  if (heapSizeInBytes > 0) {
72144  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
72145  if (pHeap == NULL) {
72146  return MA_OUT_OF_MEMORY;
72147  }
72148  } else {
72149  pHeap = NULL;
72150  }
72151 
72152  result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);
72153  if (result != MA_SUCCESS) {
72154  ma_free(pHeap, pAllocationCallbacks);
72155  return result;
72156  }
72157 
72158  ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;
72159  return MA_SUCCESS;
72160 }
72161 
72162 MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
72163 {
72164  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72165 
72166  if (pNodeBase == NULL) {
72167  return;
72168  }
72169 
72170  /*
72171  The first thing we need to do is fully detach the node. This will detach all inputs and
72172  outputs. We need to do this first because it will sever the connection with the node graph and
72173  allow us to complete uninitialization without needing to worry about thread-safety with the
72174  audio thread. The detachment process will wait for any local processing of the node to finish.
72175  */
72176  ma_node_detach_full(pNode);
72177 
72178  /*
72179  At this point the node should be completely unreferenced by the node graph and we can finish up
72180  the uninitialization process without needing to worry about thread-safety.
72181  */
72182  if (pNodeBase->_ownsHeap) {
72183  ma_free(pNodeBase->_pHeap, pAllocationCallbacks);
72184  }
72185 }
72186 
72187 MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode)
72188 {
72189  if (pNode == NULL) {
72190  return NULL;
72191  }
72192 
72193  return ((const ma_node_base*)pNode)->pNodeGraph;
72194 }
72195 
72196 MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode)
72197 {
72198  if (pNode == NULL) {
72199  return 0;
72200  }
72201 
72202  return ((ma_node_base*)pNode)->inputBusCount;
72203 }
72204 
72205 MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode)
72206 {
72207  if (pNode == NULL) {
72208  return 0;
72209  }
72210 
72211  return ((ma_node_base*)pNode)->outputBusCount;
72212 }
72213 
72214 
72215 MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex)
72216 {
72217  const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
72218 
72219  if (pNode == NULL) {
72220  return 0;
72221  }
72222 
72223  if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) {
72224  return 0; /* Invalid bus index. */
72225  }
72226 
72227  return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]);
72228 }
72229 
72230 MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex)
72231 {
72232  const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
72233 
72234  if (pNode == NULL) {
72235  return 0;
72236  }
72237 
72238  if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
72239  return 0; /* Invalid bus index. */
72240  }
72241 
72242  return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]);
72243 }
72244 
72245 
72246 static ma_result ma_node_detach_full(ma_node* pNode)
72247 {
72248  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72249  ma_uint32 iInputBus;
72250 
72251  if (pNodeBase == NULL) {
72252  return MA_INVALID_ARGS;
72253  }
72254 
72255  /*
72256  Make sure the node is completely detached first. This will not return until the output bus is
72257  guaranteed to no longer be referenced by the audio thread.
72258  */
72259  ma_node_detach_all_output_buses(pNode);
72260 
72261  /*
72262  At this point all output buses will have been detached from the graph and we can be guaranteed
72263  that none of it's input nodes will be getting processed by the graph. We can detach these
72264  without needing to worry about the audio thread touching them.
72265  */
72266  for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {
72267  ma_node_input_bus* pInputBus;
72268  ma_node_output_bus* pOutputBus;
72269 
72270  pInputBus = &pNodeBase->pInputBuses[iInputBus];
72271 
72272  /*
72273  This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those
72274  functions are specifically for the audio thread. We'll instead just manually iterate using standard
72275  linked list logic. We don't need to worry about the audio thread referencing these because the step
72276  above severed the connection to the graph.
72277  */
72278  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)) {
72279  ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */
72280  }
72281  }
72282 
72283  return MA_SUCCESS;
72284 }
72285 
72286 MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex)
72287 {
72288  ma_result result = MA_SUCCESS;
72289  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72290  ma_node_base* pInputNodeBase;
72291 
72292  if (pNode == NULL) {
72293  return MA_INVALID_ARGS;
72294  }
72295 
72296  if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
72297  return MA_INVALID_ARGS; /* Invalid output bus index. */
72298  }
72299 
72300  /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */
72301  ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);
72302  {
72303  pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;
72304  if (pInputNodeBase != NULL) {
72305  ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]);
72306  }
72307  }
72308  ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]);
72309 
72310  return result;
72311 }
72312 
72313 MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode)
72314 {
72315  ma_uint32 iOutputBus;
72316 
72317  if (pNode == NULL) {
72318  return MA_INVALID_ARGS;
72319  }
72320 
72321  for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) {
72322  ma_node_detach_output_bus(pNode, iOutputBus);
72323  }
72324 
72325  return MA_SUCCESS;
72326 }
72327 
72328 MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex)
72329 {
72330  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72331  ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode;
72332 
72333  if (pNodeBase == NULL || pOtherNodeBase == NULL) {
72334  return MA_INVALID_ARGS;
72335  }
72336 
72337  if (pNodeBase == pOtherNodeBase) {
72338  return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */
72339  }
72340 
72341  if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) {
72342  return MA_INVALID_OPERATION; /* Invalid bus index. */
72343  }
72344 
72345  /* The output channel count of the output node must be the same as the input channel count of the input node. */
72346  if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) {
72347  return MA_INVALID_OPERATION; /* Channel count is incompatible. */
72348  }
72349 
72350  /* This will deal with detaching if the output bus is already attached to something. */
72351  ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex);
72352 
72353  return MA_SUCCESS;
72354 }
72355 
72356 MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume)
72357 {
72358  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72359 
72360  if (pNodeBase == NULL) {
72361  return MA_INVALID_ARGS;
72362  }
72363 
72364  if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
72365  return MA_INVALID_ARGS; /* Invalid bus index. */
72366  }
72367 
72368  return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume);
72369 }
72370 
72371 MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex)
72372 {
72373  const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
72374 
72375  if (pNodeBase == NULL) {
72376  return 0;
72377  }
72378 
72379  if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
72380  return 0; /* Invalid bus index. */
72381  }
72382 
72383  return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]);
72384 }
72385 
72386 MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state)
72387 {
72388  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72389 
72390  if (pNodeBase == NULL) {
72391  return MA_INVALID_ARGS;
72392  }
72393 
72394  ma_atomic_exchange_i32(&pNodeBase->state, state);
72395 
72396  return MA_SUCCESS;
72397 }
72398 
72399 MA_API ma_node_state ma_node_get_state(const ma_node* pNode)
72400 {
72401  const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
72402 
72403  if (pNodeBase == NULL) {
72404  return ma_node_state_stopped;
72405  }
72406 
72407  return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state);
72408 }
72409 
72410 MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime)
72411 {
72412  if (pNode == NULL) {
72413  return MA_INVALID_ARGS;
72414  }
72415 
72416  /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
72417  if (state != ma_node_state_started && state != ma_node_state_stopped) {
72418  return MA_INVALID_ARGS;
72419  }
72420 
72421  ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime);
72422 
72423  return MA_SUCCESS;
72424 }
72425 
72426 MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state)
72427 {
72428  if (pNode == NULL) {
72429  return 0;
72430  }
72431 
72432  /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
72433  if (state != ma_node_state_started && state != ma_node_state_stopped) {
72434  return 0;
72435  }
72436 
72437  return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]);
72438 }
72439 
72440 MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime)
72441 {
72442  if (pNode == NULL) {
72443  return ma_node_state_stopped;
72444  }
72445 
72446  return ma_node_get_state_by_time_range(pNode, globalTime, globalTime);
72447 }
72448 
72449 MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)
72450 {
72451  ma_node_state state;
72452 
72453  if (pNode == NULL) {
72454  return ma_node_state_stopped;
72455  }
72456 
72457  state = ma_node_get_state(pNode);
72458 
72459  /* An explicitly stopped node is always stopped. */
72460  if (state == ma_node_state_stopped) {
72461  return ma_node_state_stopped;
72462  }
72463 
72464  /*
72465  Getting here means the node is marked as started, but it may still not be truly started due to
72466  it's start time not having been reached yet. Also, the stop time may have also been reached in
72467  which case it'll be considered stopped.
72468  */
72469  if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) {
72470  return ma_node_state_stopped; /* Start time has not yet been reached. */
72471  }
72472 
72473  if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) {
72474  return ma_node_state_stopped; /* Stop time has been reached. */
72475  }
72476 
72477  /* Getting here means the node is marked as started and is within it's start/stop times. */
72478  return ma_node_state_started;
72479 }
72480 
72481 MA_API ma_uint64 ma_node_get_time(const ma_node* pNode)
72482 {
72483  if (pNode == NULL) {
72484  return 0;
72485  }
72486 
72487  return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime);
72488 }
72489 
72490 MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime)
72491 {
72492  if (pNode == NULL) {
72493  return MA_INVALID_ARGS;
72494  }
72495 
72496  ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime);
72497 
72498  return MA_SUCCESS;
72499 }
72500 
72501 
72502 
72503 static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72504 {
72505  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72506 
72507  MA_ASSERT(pNode != NULL);
72508 
72509  if (pNodeBase->vtable->onProcess) {
72510  pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
72511  }
72512 }
72513 
72514 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)
72515 {
72516  ma_node_base* pNodeBase = (ma_node_base*)pNode;
72517  ma_result result = MA_SUCCESS;
72518  ma_uint32 iInputBus;
72519  ma_uint32 iOutputBus;
72520  ma_uint32 inputBusCount;
72521  ma_uint32 outputBusCount;
72522  ma_uint32 totalFramesRead = 0;
72523  float* ppFramesIn[MA_MAX_NODE_BUS_COUNT];
72524  float* ppFramesOut[MA_MAX_NODE_BUS_COUNT];
72525  ma_uint64 globalTimeBeg;
72526  ma_uint64 globalTimeEnd;
72527  ma_uint64 startTime;
72528  ma_uint64 stopTime;
72529  ma_uint32 timeOffsetBeg;
72530  ma_uint32 timeOffsetEnd;
72531  ma_uint32 frameCountIn;
72532  ma_uint32 frameCountOut;
72533 
72534  /*
72535  pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and
72536  expected that the number of frames read may be different to that requested. Therefore, the caller
72537  must look at this value to correctly determine how many frames were read.
72538  */
72539  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. */
72540  if (pFramesRead == NULL) {
72541  return MA_INVALID_ARGS;
72542  }
72543 
72544  *pFramesRead = 0; /* Safety. */
72545 
72546  if (pNodeBase == NULL) {
72547  return MA_INVALID_ARGS;
72548  }
72549 
72550  if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) {
72551  return MA_INVALID_ARGS; /* Invalid output bus index. */
72552  }
72553 
72554  /* Don't do anything if we're in a stopped state. */
72555  if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) {
72556  return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */
72557  }
72558 
72559 
72560  globalTimeBeg = globalTime;
72561  globalTimeEnd = globalTime + frameCount;
72562  startTime = ma_node_get_state_time(pNode, ma_node_state_started);
72563  stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped);
72564 
72565  /*
72566  At this point we know that we are inside our start/stop times. However, we may need to adjust
72567  our frame count and output pointer to accommodate since we could be straddling the time period
72568  that this function is getting called for.
72569 
72570  It's possible (and likely) that the start time does not line up with the output buffer. We
72571  therefore need to offset it by a number of frames to accommodate. The same thing applies for
72572  the stop time.
72573  */
72574  timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0;
72575  timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0;
72576 
72577  /* Trim based on the start offset. We need to silence the start of the buffer. */
72578  if (timeOffsetBeg > 0) {
72579  ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
72580  pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex);
72581  frameCount -= timeOffsetBeg;
72582  }
72583 
72584  /* 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. */
72585  if (timeOffsetEnd > 0) {
72586  frameCount -= timeOffsetEnd;
72587  }
72588 
72589 
72590  /* We run on different paths depending on the bus counts. */
72591  inputBusCount = ma_node_get_input_bus_count(pNode);
72592  outputBusCount = ma_node_get_output_bus_count(pNode);
72593 
72594  /*
72595  Run a simplified path when there are no inputs and one output. In this case there's nothing to
72596  actually read and we can go straight to output. This is a very common scenario because the vast
72597  majority of data source nodes will use this setup so this optimization I think is worthwhile.
72598  */
72599  if (inputBusCount == 0 && outputBusCount == 1) {
72600  /* Fast path. No need to read from input and no need for any caching. */
72601  frameCountIn = 0;
72602  frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */
72603 
72604  ppFramesOut[0] = pFramesOut;
72605 
72606  /*
72607  If it's a passthrough we won't be expecting the callback to output anything, so we'll
72608  need to pre-silence the output buffer.
72609  */
72610  if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
72611  ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
72612  }
72613 
72614  ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
72615  totalFramesRead = frameCountOut;
72616  } else {
72617  /* Slow path. Need to read input data. */
72618  if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
72619  /*
72620  Fast path. We're running a passthrough. We need to read directly into the output buffer, but
72621  still fire the callback so that event handling and trigger nodes can do their thing. Since
72622  it's a passthrough there's no need for any kind of caching logic.
72623  */
72624  MA_ASSERT(outputBusCount == inputBusCount);
72625  MA_ASSERT(outputBusCount == 1);
72626  MA_ASSERT(outputBusIndex == 0);
72627 
72628  /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */
72629  ppFramesOut[0] = pFramesOut;
72630  ppFramesIn[0] = ppFramesOut[0];
72631 
72632  result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime);
72633  if (result == MA_SUCCESS) {
72634  /* Even though it's a passthrough, we still need to fire the callback. */
72635  frameCountIn = totalFramesRead;
72636  frameCountOut = totalFramesRead;
72637 
72638  if (totalFramesRead > 0) {
72639  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. */
72640  }
72641 
72642  /*
72643  A passthrough should never have modified the input and output frame counts. If you're
72644  triggering these assers you need to fix your processing callback.
72645  */
72646  MA_ASSERT(frameCountIn == totalFramesRead);
72647  MA_ASSERT(frameCountOut == totalFramesRead);
72648  }
72649  } else {
72650  /* Slow path. Need to do caching. */
72651  ma_uint32 framesToProcessIn;
72652  ma_uint32 framesToProcessOut;
72653  ma_bool32 consumeNullInput = MA_FALSE;
72654 
72655  /*
72656  We use frameCount as a basis for the number of frames to read since that's what's being
72657  requested, however we still need to clamp it to whatever can fit in the cache.
72658 
72659  This will also be used as the basis for determining how many input frames to read. This is
72660  not ideal because it can result in too many input frames being read which introduces latency.
72661  To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount
72662  which is used as hint to miniaudio as to how many input frames it needs to read at a time. This
72663  callback is completely optional, and if it's not set, miniaudio will assume `frameCount`.
72664 
72665  This function will be called multiple times for each period of time, once for each output node.
72666  We cannot read from each input node each time this function is called. Instead we need to check
72667  whether or not this is first output bus to be read from for this time period, and if so, read
72668  from our input data.
72669 
72670  To determine whether or not we're ready to read data, we check a flag. There will be one flag
72671  for each output. When the flag is set, it means data has been read previously and that we're
72672  ready to advance time forward for our input nodes by reading fresh data.
72673  */
72674  framesToProcessOut = frameCount;
72675  if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) {
72676  framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus;
72677  }
72678 
72679  framesToProcessIn = frameCount;
72680  if (pNodeBase->vtable->onGetRequiredInputFrameCount) {
72681  pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */
72682  }
72683  if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) {
72684  framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus;
72685  }
72686 
72687 
72688  MA_ASSERT(framesToProcessIn <= 0xFFFF);
72689  MA_ASSERT(framesToProcessOut <= 0xFFFF);
72690 
72691  if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) {
72692  /* Getting here means we need to do another round of processing. */
72693  pNodeBase->cachedFrameCountOut = 0;
72694 
72695  for (;;) {
72696  frameCountOut = 0;
72697 
72698  /*
72699  We need to prepare our output frame pointers for processing. In the same iteration we need
72700  to mark every output bus as unread so that future calls to this function for different buses
72701  for the current time period don't pull in data when they should instead be reading from cache.
72702  */
72703  for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) {
72704  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. */
72705  ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus);
72706  }
72707 
72708  /* We only need to read from input buses if there isn't already some data in the cache. */
72709  if (pNodeBase->cachedFrameCountIn == 0) {
72710  ma_uint32 maxFramesReadIn = 0;
72711 
72712  /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */
72713  for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
72714  ma_uint32 framesRead;
72715 
72716  /* The first thing to do is get the offset within our bulk allocation to store this input data. */
72717  ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus);
72718 
72719  /* 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. */
72720  result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime);
72721  if (result != MA_SUCCESS) {
72722  /* It doesn't really matter if we fail because we'll just fill with silence. */
72723  framesRead = 0; /* Just for safety, but I don't think it's really needed. */
72724  }
72725 
72726  /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */
72727  /* Any leftover frames need to silenced for safety. */
72728  if (framesRead < framesToProcessIn) {
72729  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));
72730  }
72731 
72732  maxFramesReadIn = ma_max(maxFramesReadIn, framesRead);
72733  }
72734 
72735  /* This was a fresh load of input data so reset our consumption counter. */
72736  pNodeBase->consumedFrameCountIn = 0;
72737 
72738  /*
72739  We don't want to keep processing if there's nothing to process, so set the number of cached
72740  input frames to the maximum number we read from each attachment (the lesser will be padded
72741  with silence). If we didn't read anything, this will be set to 0 and the entire buffer will
72742  have been assigned to silence. This being equal to 0 is an important property for us because
72743  it allows us to detect when NULL can be passed into the processing callback for the input
72744  buffer for the purpose of continuous processing.
72745  */
72746  pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn;
72747  } else {
72748  /* We don't need to read anything, but we do need to prepare our input frame pointers. */
72749  for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
72750  ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus));
72751  }
72752  }
72753 
72754  /*
72755  At this point we have our input data so now we need to do some processing. Sneaky little
72756  optimization here - we can set the pointer to the output buffer for this output bus so
72757  that the final copy into the output buffer is done directly by onProcess().
72758  */
72759  if (pFramesOut != NULL) {
72760  ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex));
72761  }
72762 
72763 
72764  /* Give the processing function the entire capacity of the output buffer. */
72765  frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut);
72766 
72767  /*
72768  We need to treat nodes with continuous processing a little differently. For these ones,
72769  we always want to fire the callback with the requested number of frames, regardless of
72770  pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass
72771  in NULL for the input buffer to the callback.
72772  */
72773  if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) {
72774  /* We're using continuous processing. Make sure we specify the whole frame count at all times. */
72775  frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */
72776 
72777  if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) {
72778  consumeNullInput = MA_TRUE;
72779  } else {
72780  consumeNullInput = MA_FALSE;
72781  }
72782 
72783  /*
72784  Since we're using continuous processing we're always passing in a full frame count
72785  regardless of how much input data was read. If this is greater than what we read as
72786  input, we'll end up with an underflow. We instead need to make sure our cached frame
72787  count is set to the number of frames we'll be passing to the data callback. Not
72788  doing this will result in an underflow when we "consume" the cached data later on.
72789 
72790  Note that this check needs to be done after the "consumeNullInput" check above because
72791  we use the property of cachedFrameCountIn being 0 to determine whether or not we
72792  should be passing in a null pointer to the processing callback for when the node is
72793  configured with MA_NODE_FLAG_ALLOW_NULL_INPUT.
72794  */
72795  if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) {
72796  pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn;
72797  }
72798  } else {
72799  frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */
72800  consumeNullInput = MA_FALSE;
72801  }
72802 
72803  /*
72804  Process data slightly differently depending on whether or not we're consuming NULL
72805  input (checked just above).
72806  */
72807  if (consumeNullInput) {
72808  ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
72809  } else {
72810  /*
72811  We want to skip processing if there's no input data, but we can only do that safely if
72812  we know that there is no chance of any output frames being produced. If continuous
72813  processing is being used, this won't be a problem because the input frame count will
72814  always be non-0. However, if continuous processing is *not* enabled and input and output
72815  data is processed at different rates, we still need to process that last input frame
72816  because there could be a few excess output frames needing to be produced from cached
72817  data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for
72818  determining whether or not we need to process the node even when there are no input
72819  frames available right now.
72820  */
72821  if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {
72822  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. */
72823  } else {
72824  frameCountOut = 0; /* No data was processed. */
72825  }
72826  }
72827 
72828  /*
72829  Thanks to our sneaky optimization above we don't need to do any data copying directly into
72830  the output buffer - the onProcess() callback just did that for us. We do, however, need to
72831  apply the number of input and output frames that were processed. Note that due to continuous
72832  processing above, we need to do explicit checks here. If we just consumed a NULL input
72833  buffer it means that no actual input data was processed from the internal buffers and we
72834  don't want to be modifying any counters.
72835  */
72836  if (consumeNullInput == MA_FALSE) {
72837  pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn;
72838  pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn;
72839  }
72840 
72841  /* The cached output frame count is always equal to what we just read. */
72842  pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut;
72843 
72844  /* 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. */
72845  if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) {
72846  break;
72847  }
72848  }
72849  } else {
72850  /*
72851  We're not needing to read anything from the input buffer so just read directly from our
72852  already-processed data.
72853  */
72854  if (pFramesOut != NULL) {
72855  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));
72856  }
72857  }
72858 
72859  /* The number of frames read is always equal to the number of cached output frames. */
72860  totalFramesRead = pNodeBase->cachedFrameCountOut;
72861 
72862  /* Now that we've read the data, make sure our read flag is set. */
72863  ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE);
72864  }
72865  }
72866 
72867  /* Apply volume, if necessary. */
72868  ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]));
72869 
72870  /* Advance our local time forward. */
72871  ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead);
72872 
72873  *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */
72874  return result;
72875 }
72876 
72877 
72878 
72879 
72880 /* Data source node. */
72881 MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource)
72882 {
72884 
72885  MA_ZERO_OBJECT(&config);
72886  config.nodeConfig = ma_node_config_init();
72887  config.pDataSource = pDataSource;
72888 
72889  return config;
72890 }
72891 
72892 
72893 static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72894 {
72895  ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode;
72896  ma_format format;
72897  ma_uint32 channels;
72898  ma_uint32 frameCount;
72899  ma_uint64 framesRead = 0;
72900 
72901  MA_ASSERT(pDataSourceNode != NULL);
72902  MA_ASSERT(pDataSourceNode->pDataSource != NULL);
72903  MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0);
72904  MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1);
72905 
72906  /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */
72907  (void)ppFramesIn;
72908  (void)pFrameCountIn;
72909 
72910  frameCount = *pFrameCountOut;
72911 
72912  /* miniaudio should never be calling this with a frame count of zero. */
72913  MA_ASSERT(frameCount > 0);
72914 
72915  if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */
72916  /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */
72917  MA_ASSERT(format == ma_format_f32);
72918  (void)format; /* Just to silence some static analysis tools. */
72919 
72920  ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead);
72921  }
72922 
72923  *pFrameCountOut = (ma_uint32)framesRead;
72924 }
72925 
72926 static ma_node_vtable g_ma_data_source_node_vtable =
72927 {
72928  ma_data_source_node_process_pcm_frames,
72929  NULL, /* onGetRequiredInputFrameCount */
72930  0, /* 0 input buses. */
72931  1, /* 1 output bus. */
72932  0
72933 };
72934 
72935 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)
72936 {
72937  ma_result result;
72938  ma_format format; /* For validating the format, which must be ma_format_f32. */
72939  ma_uint32 channels; /* For specifying the channel count of the output bus. */
72940  ma_node_config baseConfig;
72941 
72942  if (pDataSourceNode == NULL) {
72943  return MA_INVALID_ARGS;
72944  }
72945 
72946  MA_ZERO_OBJECT(pDataSourceNode);
72947 
72948  if (pConfig == NULL) {
72949  return MA_INVALID_ARGS;
72950  }
72951 
72952  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. */
72953  if (result != MA_SUCCESS) {
72954  return result;
72955  }
72956 
72957  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. */
72958  if (format != ma_format_f32) {
72959  return MA_INVALID_ARGS; /* Invalid format. */
72960  }
72961 
72962  /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */
72963  baseConfig = pConfig->nodeConfig;
72964  baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */
72965 
72966  /*
72967  The channel count is defined by the data source. It is invalid for the caller to manually set
72968  the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the
72969  channel count pointer to NULL which is how it must remain. If you trigger any of these asserts
72970  it means you're explicitly setting the channel count. Instead, configure the output channel
72971  count of your data source to be the necessary channel count.
72972  */
72973  if (baseConfig.pOutputChannels != NULL) {
72974  return MA_INVALID_ARGS;
72975  }
72976 
72977  baseConfig.pOutputChannels = &channels;
72978 
72979  result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base);
72980  if (result != MA_SUCCESS) {
72981  return result;
72982  }
72983 
72984  pDataSourceNode->pDataSource = pConfig->pDataSource;
72985 
72986  return MA_SUCCESS;
72987 }
72988 
72989 MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks)
72990 {
72991  ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks);
72992 }
72993 
72994 MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping)
72995 {
72996  if (pDataSourceNode == NULL) {
72997  return MA_INVALID_ARGS;
72998  }
72999 
73000  return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping);
73001 }
73002 
73003 MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode)
73004 {
73005  if (pDataSourceNode == NULL) {
73006  return MA_FALSE;
73007  }
73008 
73009  return ma_data_source_is_looping(pDataSourceNode->pDataSource);
73010 }
73011 
73012 
73013 
73014 /* Splitter Node. */
73015 MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels)
73016 {
73017  ma_splitter_node_config config;
73018 
73019  MA_ZERO_OBJECT(&config);
73020  config.nodeConfig = ma_node_config_init();
73021  config.channels = channels;
73022  config.outputBusCount = 2;
73023 
73024  return config;
73025 }
73026 
73027 
73028 static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73029 {
73030  ma_node_base* pNodeBase = (ma_node_base*)pNode;
73031  ma_uint32 iOutputBus;
73032  ma_uint32 channels;
73033 
73034  MA_ASSERT(pNodeBase != NULL);
73035  MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1);
73036 
73037  /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */
73038  (void)pFrameCountIn;
73039 
73040  /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */
73041  channels = ma_node_get_input_channels(pNodeBase, 0);
73042 
73043  /* Splitting is just copying the first input bus and copying it over to each output bus. */
73044  for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
73045  ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels);
73046  }
73047 }
73048 
73049 static ma_node_vtable g_ma_splitter_node_vtable =
73050 {
73051  ma_splitter_node_process_pcm_frames,
73052  NULL, /* onGetRequiredInputFrameCount */
73053  1, /* 1 input bus. */
73054  MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */
73055  0
73056 };
73057 
73058 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)
73059 {
73060  ma_result result;
73061  ma_node_config baseConfig;
73062  ma_uint32 pInputChannels[1];
73063  ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT];
73064  ma_uint32 iOutputBus;
73065 
73066  if (pSplitterNode == NULL) {
73067  return MA_INVALID_ARGS;
73068  }
73069 
73070  MA_ZERO_OBJECT(pSplitterNode);
73071 
73072  if (pConfig == NULL) {
73073  return MA_INVALID_ARGS;
73074  }
73075 
73076  if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) {
73077  return MA_INVALID_ARGS; /* Too many output buses. */
73078  }
73079 
73080  /* Splitters require the same number of channels between inputs and outputs. */
73081  pInputChannels[0] = pConfig->channels;
73082  for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) {
73083  pOutputChannels[iOutputBus] = pConfig->channels;
73084  }
73085 
73086  baseConfig = pConfig->nodeConfig;
73087  baseConfig.vtable = &g_ma_splitter_node_vtable;
73088  baseConfig.pInputChannels = pInputChannels;
73089  baseConfig.pOutputChannels = pOutputChannels;
73090  baseConfig.outputBusCount = pConfig->outputBusCount;
73091 
73092  result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base);
73093  if (result != MA_SUCCESS) {
73094  return result; /* Failed to initialize the base node. */
73095  }
73096 
73097  return MA_SUCCESS;
73098 }
73099 
73100 MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks)
73101 {
73102  ma_node_uninit(pSplitterNode, pAllocationCallbacks);
73103 }
73104 
73105 
73106 /*
73107 Biquad Node
73108 */
73109 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)
73110 {
73111  ma_biquad_node_config config;
73112 
73113  config.nodeConfig = ma_node_config_init();
73114  config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
73115 
73116  return config;
73117 }
73118 
73119 static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73120 {
73121  ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
73122 
73123  MA_ASSERT(pNode != NULL);
73124  (void)pFrameCountIn;
73125 
73126  ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73127 }
73128 
73129 static ma_node_vtable g_ma_biquad_node_vtable =
73130 {
73131  ma_biquad_node_process_pcm_frames,
73132  NULL, /* onGetRequiredInputFrameCount */
73133  1, /* One input. */
73134  1, /* One output. */
73135  0 /* Default flags. */
73136 };
73137 
73138 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)
73139 {
73140  ma_result result;
73141  ma_node_config baseNodeConfig;
73142 
73143  if (pNode == NULL) {
73144  return MA_INVALID_ARGS;
73145  }
73146 
73147  MA_ZERO_OBJECT(pNode);
73148 
73149  if (pConfig == NULL) {
73150  return MA_INVALID_ARGS;
73151  }
73152 
73153  if (pConfig->biquad.format != ma_format_f32) {
73154  return MA_INVALID_ARGS; /* The format must be f32. */
73155  }
73156 
73157  result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad);
73158  if (result != MA_SUCCESS) {
73159  return result;
73160  }
73161 
73162  baseNodeConfig = ma_node_config_init();
73163  baseNodeConfig.vtable = &g_ma_biquad_node_vtable;
73164  baseNodeConfig.pInputChannels = &pConfig->biquad.channels;
73165  baseNodeConfig.pOutputChannels = &pConfig->biquad.channels;
73166 
73167  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73168  if (result != MA_SUCCESS) {
73169  return result;
73170  }
73171 
73172  return result;
73173 }
73174 
73175 MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode)
73176 {
73177  ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
73178 
73179  MA_ASSERT(pNode != NULL);
73180 
73181  return ma_biquad_reinit(pConfig, &pLPFNode->biquad);
73182 }
73183 
73184 MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73185 {
73186  ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
73187 
73188  if (pNode == NULL) {
73189  return;
73190  }
73191 
73192  ma_node_uninit(pNode, pAllocationCallbacks);
73193  ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks);
73194 }
73195 
73196 
73197 
73198 /*
73199 Low Pass Filter Node
73200 */
73201 MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
73202 {
73203  ma_lpf_node_config config;
73204 
73205  config.nodeConfig = ma_node_config_init();
73206  config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
73207 
73208  return config;
73209 }
73210 
73211 static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73212 {
73213  ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
73214 
73215  MA_ASSERT(pNode != NULL);
73216  (void)pFrameCountIn;
73217 
73218  ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73219 }
73220 
73221 static ma_node_vtable g_ma_lpf_node_vtable =
73222 {
73223  ma_lpf_node_process_pcm_frames,
73224  NULL, /* onGetRequiredInputFrameCount */
73225  1, /* One input. */
73226  1, /* One output. */
73227  0 /* Default flags. */
73228 };
73229 
73230 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)
73231 {
73232  ma_result result;
73233  ma_node_config baseNodeConfig;
73234 
73235  if (pNode == NULL) {
73236  return MA_INVALID_ARGS;
73237  }
73238 
73239  MA_ZERO_OBJECT(pNode);
73240 
73241  if (pConfig == NULL) {
73242  return MA_INVALID_ARGS;
73243  }
73244 
73245  if (pConfig->lpf.format != ma_format_f32) {
73246  return MA_INVALID_ARGS; /* The format must be f32. */
73247  }
73248 
73249  result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf);
73250  if (result != MA_SUCCESS) {
73251  return result;
73252  }
73253 
73254  baseNodeConfig = ma_node_config_init();
73255  baseNodeConfig.vtable = &g_ma_lpf_node_vtable;
73256  baseNodeConfig.pInputChannels = &pConfig->lpf.channels;
73257  baseNodeConfig.pOutputChannels = &pConfig->lpf.channels;
73258 
73259  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73260  if (result != MA_SUCCESS) {
73261  return result;
73262  }
73263 
73264  return result;
73265 }
73266 
73267 MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode)
73268 {
73269  ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
73270 
73271  if (pNode == NULL) {
73272  return MA_INVALID_ARGS;
73273  }
73274 
73275  return ma_lpf_reinit(pConfig, &pLPFNode->lpf);
73276 }
73277 
73278 MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73279 {
73280  ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
73281 
73282  if (pNode == NULL) {
73283  return;
73284  }
73285 
73286  ma_node_uninit(pNode, pAllocationCallbacks);
73287  ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks);
73288 }
73289 
73290 
73291 
73292 /*
73293 High Pass Filter Node
73294 */
73295 MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
73296 {
73297  ma_hpf_node_config config;
73298 
73299  config.nodeConfig = ma_node_config_init();
73300  config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
73301 
73302  return config;
73303 }
73304 
73305 static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73306 {
73307  ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
73308 
73309  MA_ASSERT(pNode != NULL);
73310  (void)pFrameCountIn;
73311 
73312  ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73313 }
73314 
73315 static ma_node_vtable g_ma_hpf_node_vtable =
73316 {
73317  ma_hpf_node_process_pcm_frames,
73318  NULL, /* onGetRequiredInputFrameCount */
73319  1, /* One input. */
73320  1, /* One output. */
73321  0 /* Default flags. */
73322 };
73323 
73324 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)
73325 {
73326  ma_result result;
73327  ma_node_config baseNodeConfig;
73328 
73329  if (pNode == NULL) {
73330  return MA_INVALID_ARGS;
73331  }
73332 
73333  MA_ZERO_OBJECT(pNode);
73334 
73335  if (pConfig == NULL) {
73336  return MA_INVALID_ARGS;
73337  }
73338 
73339  if (pConfig->hpf.format != ma_format_f32) {
73340  return MA_INVALID_ARGS; /* The format must be f32. */
73341  }
73342 
73343  result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf);
73344  if (result != MA_SUCCESS) {
73345  return result;
73346  }
73347 
73348  baseNodeConfig = ma_node_config_init();
73349  baseNodeConfig.vtable = &g_ma_hpf_node_vtable;
73350  baseNodeConfig.pInputChannels = &pConfig->hpf.channels;
73351  baseNodeConfig.pOutputChannels = &pConfig->hpf.channels;
73352 
73353  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73354  if (result != MA_SUCCESS) {
73355  return result;
73356  }
73357 
73358  return result;
73359 }
73360 
73361 MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode)
73362 {
73363  ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
73364 
73365  if (pNode == NULL) {
73366  return MA_INVALID_ARGS;
73367  }
73368 
73369  return ma_hpf_reinit(pConfig, &pHPFNode->hpf);
73370 }
73371 
73372 MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73373 {
73374  ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
73375 
73376  if (pNode == NULL) {
73377  return;
73378  }
73379 
73380  ma_node_uninit(pNode, pAllocationCallbacks);
73381  ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks);
73382 }
73383 
73384 
73385 
73386 
73387 /*
73388 Band Pass Filter Node
73389 */
73390 MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
73391 {
73392  ma_bpf_node_config config;
73393 
73394  config.nodeConfig = ma_node_config_init();
73395  config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
73396 
73397  return config;
73398 }
73399 
73400 static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73401 {
73402  ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
73403 
73404  MA_ASSERT(pNode != NULL);
73405  (void)pFrameCountIn;
73406 
73407  ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73408 }
73409 
73410 static ma_node_vtable g_ma_bpf_node_vtable =
73411 {
73412  ma_bpf_node_process_pcm_frames,
73413  NULL, /* onGetRequiredInputFrameCount */
73414  1, /* One input. */
73415  1, /* One output. */
73416  0 /* Default flags. */
73417 };
73418 
73419 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)
73420 {
73421  ma_result result;
73422  ma_node_config baseNodeConfig;
73423 
73424  if (pNode == NULL) {
73425  return MA_INVALID_ARGS;
73426  }
73427 
73428  MA_ZERO_OBJECT(pNode);
73429 
73430  if (pConfig == NULL) {
73431  return MA_INVALID_ARGS;
73432  }
73433 
73434  if (pConfig->bpf.format != ma_format_f32) {
73435  return MA_INVALID_ARGS; /* The format must be f32. */
73436  }
73437 
73438  result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf);
73439  if (result != MA_SUCCESS) {
73440  return result;
73441  }
73442 
73443  baseNodeConfig = ma_node_config_init();
73444  baseNodeConfig.vtable = &g_ma_bpf_node_vtable;
73445  baseNodeConfig.pInputChannels = &pConfig->bpf.channels;
73446  baseNodeConfig.pOutputChannels = &pConfig->bpf.channels;
73447 
73448  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73449  if (result != MA_SUCCESS) {
73450  return result;
73451  }
73452 
73453  return result;
73454 }
73455 
73456 MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode)
73457 {
73458  ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
73459 
73460  if (pNode == NULL) {
73461  return MA_INVALID_ARGS;
73462  }
73463 
73464  return ma_bpf_reinit(pConfig, &pBPFNode->bpf);
73465 }
73466 
73467 MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73468 {
73469  ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
73470 
73471  if (pNode == NULL) {
73472  return;
73473  }
73474 
73475  ma_node_uninit(pNode, pAllocationCallbacks);
73476  ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks);
73477 }
73478 
73479 
73480 
73481 /*
73482 Notching Filter Node
73483 */
73484 MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
73485 {
73486  ma_notch_node_config config;
73487 
73488  config.nodeConfig = ma_node_config_init();
73489  config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency);
73490 
73491  return config;
73492 }
73493 
73494 static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73495 {
73496  ma_notch_node* pBPFNode = (ma_notch_node*)pNode;
73497 
73498  MA_ASSERT(pNode != NULL);
73499  (void)pFrameCountIn;
73500 
73501  ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73502 }
73503 
73504 static ma_node_vtable g_ma_notch_node_vtable =
73505 {
73506  ma_notch_node_process_pcm_frames,
73507  NULL, /* onGetRequiredInputFrameCount */
73508  1, /* One input. */
73509  1, /* One output. */
73510  0 /* Default flags. */
73511 };
73512 
73513 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)
73514 {
73515  ma_result result;
73516  ma_node_config baseNodeConfig;
73517 
73518  if (pNode == NULL) {
73519  return MA_INVALID_ARGS;
73520  }
73521 
73522  MA_ZERO_OBJECT(pNode);
73523 
73524  if (pConfig == NULL) {
73525  return MA_INVALID_ARGS;
73526  }
73527 
73528  if (pConfig->notch.format != ma_format_f32) {
73529  return MA_INVALID_ARGS; /* The format must be f32. */
73530  }
73531 
73532  result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch);
73533  if (result != MA_SUCCESS) {
73534  return result;
73535  }
73536 
73537  baseNodeConfig = ma_node_config_init();
73538  baseNodeConfig.vtable = &g_ma_notch_node_vtable;
73539  baseNodeConfig.pInputChannels = &pConfig->notch.channels;
73540  baseNodeConfig.pOutputChannels = &pConfig->notch.channels;
73541 
73542  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73543  if (result != MA_SUCCESS) {
73544  return result;
73545  }
73546 
73547  return result;
73548 }
73549 
73550 MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode)
73551 {
73552  ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
73553 
73554  if (pNode == NULL) {
73555  return MA_INVALID_ARGS;
73556  }
73557 
73558  return ma_notch2_reinit(pConfig, &pNotchNode->notch);
73559 }
73560 
73561 MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73562 {
73563  ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
73564 
73565  if (pNode == NULL) {
73566  return;
73567  }
73568 
73569  ma_node_uninit(pNode, pAllocationCallbacks);
73570  ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks);
73571 }
73572 
73573 
73574 
73575 /*
73576 Peaking Filter Node
73577 */
73578 MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
73579 {
73580  ma_peak_node_config config;
73581 
73582  config.nodeConfig = ma_node_config_init();
73583  config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
73584 
73585  return config;
73586 }
73587 
73588 static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73589 {
73590  ma_peak_node* pBPFNode = (ma_peak_node*)pNode;
73591 
73592  MA_ASSERT(pNode != NULL);
73593  (void)pFrameCountIn;
73594 
73595  ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73596 }
73597 
73598 static ma_node_vtable g_ma_peak_node_vtable =
73599 {
73600  ma_peak_node_process_pcm_frames,
73601  NULL, /* onGetRequiredInputFrameCount */
73602  1, /* One input. */
73603  1, /* One output. */
73604  0 /* Default flags. */
73605 };
73606 
73607 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)
73608 {
73609  ma_result result;
73610  ma_node_config baseNodeConfig;
73611 
73612  if (pNode == NULL) {
73613  return MA_INVALID_ARGS;
73614  }
73615 
73616  MA_ZERO_OBJECT(pNode);
73617 
73618  if (pConfig == NULL) {
73619  return MA_INVALID_ARGS;
73620  }
73621 
73622  if (pConfig->peak.format != ma_format_f32) {
73623  return MA_INVALID_ARGS; /* The format must be f32. */
73624  }
73625 
73626  result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak);
73627  if (result != MA_SUCCESS) {
73628  ma_node_uninit(pNode, pAllocationCallbacks);
73629  return result;
73630  }
73631 
73632  baseNodeConfig = ma_node_config_init();
73633  baseNodeConfig.vtable = &g_ma_peak_node_vtable;
73634  baseNodeConfig.pInputChannels = &pConfig->peak.channels;
73635  baseNodeConfig.pOutputChannels = &pConfig->peak.channels;
73636 
73637  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73638  if (result != MA_SUCCESS) {
73639  return result;
73640  }
73641 
73642  return result;
73643 }
73644 
73645 MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode)
73646 {
73647  ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
73648 
73649  if (pNode == NULL) {
73650  return MA_INVALID_ARGS;
73651  }
73652 
73653  return ma_peak2_reinit(pConfig, &pPeakNode->peak);
73654 }
73655 
73656 MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73657 {
73658  ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
73659 
73660  if (pNode == NULL) {
73661  return;
73662  }
73663 
73664  ma_node_uninit(pNode, pAllocationCallbacks);
73665  ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks);
73666 }
73667 
73668 
73669 
73670 /*
73671 Low Shelf Filter Node
73672 */
73673 MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
73674 {
73675  ma_loshelf_node_config config;
73676 
73677  config.nodeConfig = ma_node_config_init();
73678  config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
73679 
73680  return config;
73681 }
73682 
73683 static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73684 {
73685  ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode;
73686 
73687  MA_ASSERT(pNode != NULL);
73688  (void)pFrameCountIn;
73689 
73690  ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73691 }
73692 
73693 static ma_node_vtable g_ma_loshelf_node_vtable =
73694 {
73695  ma_loshelf_node_process_pcm_frames,
73696  NULL, /* onGetRequiredInputFrameCount */
73697  1, /* One input. */
73698  1, /* One output. */
73699  0 /* Default flags. */
73700 };
73701 
73702 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)
73703 {
73704  ma_result result;
73705  ma_node_config baseNodeConfig;
73706 
73707  if (pNode == NULL) {
73708  return MA_INVALID_ARGS;
73709  }
73710 
73711  MA_ZERO_OBJECT(pNode);
73712 
73713  if (pConfig == NULL) {
73714  return MA_INVALID_ARGS;
73715  }
73716 
73717  if (pConfig->loshelf.format != ma_format_f32) {
73718  return MA_INVALID_ARGS; /* The format must be f32. */
73719  }
73720 
73721  result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf);
73722  if (result != MA_SUCCESS) {
73723  return result;
73724  }
73725 
73726  baseNodeConfig = ma_node_config_init();
73727  baseNodeConfig.vtable = &g_ma_loshelf_node_vtable;
73728  baseNodeConfig.pInputChannels = &pConfig->loshelf.channels;
73729  baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels;
73730 
73731  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73732  if (result != MA_SUCCESS) {
73733  return result;
73734  }
73735 
73736  return result;
73737 }
73738 
73739 MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode)
73740 {
73741  ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
73742 
73743  if (pNode == NULL) {
73744  return MA_INVALID_ARGS;
73745  }
73746 
73747  return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf);
73748 }
73749 
73750 MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73751 {
73752  ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
73753 
73754  if (pNode == NULL) {
73755  return;
73756  }
73757 
73758  ma_node_uninit(pNode, pAllocationCallbacks);
73759  ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks);
73760 }
73761 
73762 
73763 
73764 /*
73765 High Shelf Filter Node
73766 */
73767 MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
73768 {
73769  ma_hishelf_node_config config;
73770 
73771  config.nodeConfig = ma_node_config_init();
73772  config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
73773 
73774  return config;
73775 }
73776 
73777 static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73778 {
73779  ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode;
73780 
73781  MA_ASSERT(pNode != NULL);
73782  (void)pFrameCountIn;
73783 
73784  ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73785 }
73786 
73787 static ma_node_vtable g_ma_hishelf_node_vtable =
73788 {
73789  ma_hishelf_node_process_pcm_frames,
73790  NULL, /* onGetRequiredInputFrameCount */
73791  1, /* One input. */
73792  1, /* One output. */
73793  0 /* Default flags. */
73794 };
73795 
73796 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)
73797 {
73798  ma_result result;
73799  ma_node_config baseNodeConfig;
73800 
73801  if (pNode == NULL) {
73802  return MA_INVALID_ARGS;
73803  }
73804 
73805  MA_ZERO_OBJECT(pNode);
73806 
73807  if (pConfig == NULL) {
73808  return MA_INVALID_ARGS;
73809  }
73810 
73811  if (pConfig->hishelf.format != ma_format_f32) {
73812  return MA_INVALID_ARGS; /* The format must be f32. */
73813  }
73814 
73815  result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf);
73816  if (result != MA_SUCCESS) {
73817  return result;
73818  }
73819 
73820  baseNodeConfig = ma_node_config_init();
73821  baseNodeConfig.vtable = &g_ma_hishelf_node_vtable;
73822  baseNodeConfig.pInputChannels = &pConfig->hishelf.channels;
73823  baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels;
73824 
73825  result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73826  if (result != MA_SUCCESS) {
73827  return result;
73828  }
73829 
73830  return result;
73831 }
73832 
73833 MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode)
73834 {
73835  ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
73836 
73837  if (pNode == NULL) {
73838  return MA_INVALID_ARGS;
73839  }
73840 
73841  return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf);
73842 }
73843 
73844 MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73845 {
73846  ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
73847 
73848  if (pNode == NULL) {
73849  return;
73850  }
73851 
73852  ma_node_uninit(pNode, pAllocationCallbacks);
73853  ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks);
73854 }
73855 
73856 
73857 
73858 
73859 MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
73860 {
73861  ma_delay_node_config config;
73862 
73863  config.nodeConfig = ma_node_config_init();
73864  config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay);
73865 
73866  return config;
73867 }
73868 
73869 
73870 static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73871 {
73872  ma_delay_node* pDelayNode = (ma_delay_node*)pNode;
73873 
73874  (void)pFrameCountIn;
73875 
73876  ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73877 }
73878 
73879 static ma_node_vtable g_ma_delay_node_vtable =
73880 {
73881  ma_delay_node_process_pcm_frames,
73882  NULL,
73883  1, /* 1 input channels. */
73884  1, /* 1 output channel. */
73885  MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */
73886 };
73887 
73888 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)
73889 {
73890  ma_result result;
73891  ma_node_config baseConfig;
73892 
73893  if (pDelayNode == NULL) {
73894  return MA_INVALID_ARGS;
73895  }
73896 
73897  MA_ZERO_OBJECT(pDelayNode);
73898 
73899  result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay);
73900  if (result != MA_SUCCESS) {
73901  return result;
73902  }
73903 
73904  baseConfig = pConfig->nodeConfig;
73905  baseConfig.vtable = &g_ma_delay_node_vtable;
73906  baseConfig.pInputChannels = &pConfig->delay.channels;
73907  baseConfig.pOutputChannels = &pConfig->delay.channels;
73908 
73909  result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode);
73910  if (result != MA_SUCCESS) {
73911  ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
73912  return result;
73913  }
73914 
73915  return result;
73916 }
73917 
73918 MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks)
73919 {
73920  if (pDelayNode == NULL) {
73921  return;
73922  }
73923 
73924  /* The base node is always uninitialized first. */
73925  ma_node_uninit(pDelayNode, pAllocationCallbacks);
73926  ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
73927 }
73928 
73929 MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value)
73930 {
73931  if (pDelayNode == NULL) {
73932  return;
73933  }
73934 
73935  ma_delay_set_wet(&pDelayNode->delay, value);
73936 }
73937 
73938 MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode)
73939 {
73940  if (pDelayNode == NULL) {
73941  return 0;
73942  }
73943 
73944  return ma_delay_get_wet(&pDelayNode->delay);
73945 }
73946 
73947 MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value)
73948 {
73949  if (pDelayNode == NULL) {
73950  return;
73951  }
73952 
73953  ma_delay_set_dry(&pDelayNode->delay, value);
73954 }
73955 
73956 MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode)
73957 {
73958  if (pDelayNode == NULL) {
73959  return 0;
73960  }
73961 
73962  return ma_delay_get_dry(&pDelayNode->delay);
73963 }
73964 
73965 MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value)
73966 {
73967  if (pDelayNode == NULL) {
73968  return;
73969  }
73970 
73971  ma_delay_set_decay(&pDelayNode->delay, value);
73972 }
73973 
73974 MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode)
73975 {
73976  if (pDelayNode == NULL) {
73977  return 0;
73978  }
73979 
73980  return ma_delay_get_decay(&pDelayNode->delay);
73981 }
73982 #endif /* MA_NO_NODE_GRAPH */
73983 
73984 
73985 /* SECTION: miniaudio_engine.c */
73986 #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
73987 /**************************************************************************************************************************************************************
73988 
73989 Engine
73990 
73991 **************************************************************************************************************************************************************/
73992 #define MA_SEEK_TARGET_NONE (~(ma_uint64)0)
73993 
73994 
73995 static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd)
73996 {
73997  MA_ASSERT(pSound != NULL);
73998  ma_atomic_exchange_32(&pSound->atEnd, atEnd);
73999 
74000  /* Fire any callbacks or events. */
74001  if (atEnd) {
74002  if (pSound->endCallback != NULL) {
74003  pSound->endCallback(pSound->pEndCallbackUserData, pSound);
74004  }
74005  }
74006 }
74007 
74008 static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound)
74009 {
74010  MA_ASSERT(pSound != NULL);
74011  return ma_atomic_load_32(&pSound->atEnd);
74012 }
74013 
74014 
74015 MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags)
74016 {
74017  ma_engine_node_config config;
74018 
74019  MA_ZERO_OBJECT(&config);
74020  config.pEngine = pEngine;
74021  config.type = type;
74022  config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0;
74023  config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0;
74024  config.monoExpansionMode = pEngine->monoExpansionMode;
74025 
74026  return config;
74027 }
74028 
74029 
74030 static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode)
74031 {
74032  ma_bool32 isUpdateRequired = MA_FALSE;
74033  float newPitch;
74034 
74035  MA_ASSERT(pEngineNode != NULL);
74036 
74037  newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire);
74038 
74039  if (pEngineNode->oldPitch != newPitch) {
74040  pEngineNode->oldPitch = newPitch;
74041  isUpdateRequired = MA_TRUE;
74042  }
74043 
74044  if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) {
74045  pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch;
74046  isUpdateRequired = MA_TRUE;
74047  }
74048 
74049  if (isUpdateRequired) {
74050  float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine);
74051  ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch);
74052  }
74053 }
74054 
74055 static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode)
74056 {
74057  MA_ASSERT(pEngineNode != NULL);
74058 
74059  /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */
74060  return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire);
74061 }
74062 
74063 static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode)
74064 {
74065  MA_ASSERT(pEngineNode != NULL);
74066 
74067  return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire);
74068 }
74069 
74070 static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount)
74071 {
74072  ma_uint64 inputFrameCount = 0;
74073 
74074  if (ma_engine_node_is_pitching_enabled(pEngineNode)) {
74075  ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount);
74076  if (result != MA_SUCCESS) {
74077  inputFrameCount = 0;
74078  }
74079  } else {
74080  inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */
74081  }
74082 
74083  return inputFrameCount;
74084 }
74085 
74086 static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume)
74087 {
74088  if (pEngineNode == NULL) {
74089  return MA_INVALID_ARGS;
74090  }
74091 
74092  ma_atomic_float_set(&pEngineNode->volume, volume);
74093 
74094  /* If we're not smoothing we should bypass the volume gainer entirely. */
74095  if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) {
74096  /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */
74097  ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume);
74098  } else {
74099  /* We're using volume smoothing, so apply the master volume to the gainer. */
74100  ma_gainer_set_gain(&pEngineNode->volumeGainer, volume);
74101  }
74102 
74103  return MA_SUCCESS;
74104 }
74105 
74106 static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume)
74107 {
74108  if (pVolume == NULL) {
74109  return MA_INVALID_ARGS;
74110  }
74111 
74112  *pVolume = 0.0f;
74113 
74114  if (pEngineNode == NULL) {
74115  return MA_INVALID_ARGS;
74116  }
74117 
74118  *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume);
74119 
74120  return MA_SUCCESS;
74121 }
74122 
74123 
74124 static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
74125 {
74126  ma_uint32 frameCountIn;
74127  ma_uint32 frameCountOut;
74128  ma_uint32 totalFramesProcessedIn;
74129  ma_uint32 totalFramesProcessedOut;
74130  ma_uint32 channelsIn;
74131  ma_uint32 channelsOut;
74132  ma_bool32 isPitchingEnabled;
74133  ma_bool32 isFadingEnabled;
74134  ma_bool32 isSpatializationEnabled;
74135  ma_bool32 isPanningEnabled;
74136  ma_bool32 isVolumeSmoothingEnabled;
74137 
74138  frameCountIn = *pFrameCountIn;
74139  frameCountOut = *pFrameCountOut;
74140 
74141  channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer);
74142  channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer);
74143 
74144  totalFramesProcessedIn = 0;
74145  totalFramesProcessedOut = 0;
74146 
74147  /* Update the fader if applicable. */
74148  {
74149  ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames);
74150  if (fadeLengthInFrames != ~(ma_uint64)0) {
74151  float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg);
74152  float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd);
74153  ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames);
74154  if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) {
74155  fadeStartOffsetInFrames = 0;
74156  } else {
74157  fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine);
74158  }
74159 
74160  ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames);
74161 
74162  /* Reset the fade length so we don't erroneously apply it again. */
74163  ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0);
74164  }
74165  }
74166 
74167  isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode);
74168  isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1;
74169  isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode);
74170  isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1;
74171  isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0;
74172 
74173  /* Keep going while we've still got data available for processing. */
74174  while (totalFramesProcessedOut < frameCountOut) {
74175  /*
74176  We need to process in a specific order. We always do resampling first because it's likely
74177  we're going to be increasing the channel count after spatialization. Also, I want to do
74178  fading based on the output sample rate.
74179 
74180  We'll first read into a buffer from the resampler. Then we'll do all processing that
74181  operates on the on the input channel count. We'll then get the spatializer to output to
74182  the output buffer and then do all effects from that point directly in the output buffer
74183  in-place.
74184 
74185  Note that we're always running the resampler if pitching is enabled, even when the pitch
74186  is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch
74187  when we move away from 1, back to 1, and then away from 1 again. We'll want to implement
74188  any pitch=1 optimizations in the resampler itself.
74189 
74190  There's a small optimization here that we'll utilize since it might be a fairly common
74191  case. When the input and output channel counts are the same, we'll read straight into the
74192  output buffer from the resampler and do everything in-place.
74193  */
74194  const float* pRunningFramesIn;
74195  float* pRunningFramesOut;
74196  float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */
74197  float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
74198  ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn;
74199  ma_uint32 framesAvailableIn;
74200  ma_uint32 framesAvailableOut;
74201  ma_uint32 framesJustProcessedIn;
74202  ma_uint32 framesJustProcessedOut;
74203  ma_bool32 isWorkingBufferValid = MA_FALSE;
74204 
74205  framesAvailableIn = frameCountIn - totalFramesProcessedIn;
74206  framesAvailableOut = frameCountOut - totalFramesProcessedOut;
74207 
74208  pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn);
74209  pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut);
74210 
74211  if (channelsIn == channelsOut) {
74212  /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */
74213  pWorkingBuffer = pRunningFramesOut;
74214  } else {
74215  /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */
74216  pWorkingBuffer = temp;
74217  if (framesAvailableOut > tempCapInFrames) {
74218  framesAvailableOut = tempCapInFrames;
74219  }
74220  }
74221 
74222  /* First is resampler. */
74223  if (isPitchingEnabled) {
74224  ma_uint64 resampleFrameCountIn = framesAvailableIn;
74225  ma_uint64 resampleFrameCountOut = framesAvailableOut;
74226 
74227  ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut);
74228  isWorkingBufferValid = MA_TRUE;
74229 
74230  framesJustProcessedIn = (ma_uint32)resampleFrameCountIn;
74231  framesJustProcessedOut = (ma_uint32)resampleFrameCountOut;
74232  } else {
74233  framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut);
74234  framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */
74235  }
74236 
74237  /* Fading. */
74238  if (isFadingEnabled) {
74239  if (isWorkingBufferValid) {
74240  ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */
74241  } else {
74242  ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
74243  isWorkingBufferValid = MA_TRUE;
74244  }
74245  }
74246 
74247  /*
74248  If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case
74249  we'll want to apply our volume now.
74250  */
74251  if (isVolumeSmoothingEnabled) {
74252  if (isWorkingBufferValid) {
74253  ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut);
74254  } else {
74255  ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
74256  isWorkingBufferValid = MA_TRUE;
74257  }
74258  }
74259 
74260  /*
74261  If at this point we still haven't actually done anything with the working buffer we need
74262  to just read straight from the input buffer.
74263  */
74264  if (isWorkingBufferValid == MA_FALSE) {
74265  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. */
74266  }
74267 
74268  /* Spatialization. */
74269  if (isSpatializationEnabled) {
74270  ma_uint32 iListener;
74271 
74272  /*
74273  When determining the listener to use, we first check to see if the sound is pinned to a
74274  specific listener. If so, we use that. Otherwise we just use the closest listener.
74275  */
74276  if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) {
74277  iListener = pEngineNode->pinnedListenerIndex;
74278  } else {
74279  ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer);
74280  iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z);
74281  }
74282 
74283  ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut);
74284  } else {
74285  /* No spatialization, but we still need to do channel conversion and master volume. */
74286  float volume;
74287  ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */
74288 
74289  if (channelsIn == channelsOut) {
74290  /* No channel conversion required. Just copy straight to the output buffer. */
74291  if (isVolumeSmoothingEnabled) {
74292  /* Volume has already been applied. Just copy straight to the output buffer. */
74293  ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut);
74294  } else {
74295  /* Volume has not been applied yet. Copy and apply volume in the same pass. */
74296  ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume);
74297  }
74298  } else {
74299  /* Channel conversion required. TODO: Add support for channel maps here. */
74300  ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode);
74301 
74302  /* If we're using smoothing, the volume will have already been applied. */
74303  if (!isVolumeSmoothingEnabled) {
74304  ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume);
74305  }
74306  }
74307  }
74308 
74309  /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */
74310 
74311  /* Panning. */
74312  if (isPanningEnabled) {
74313  ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */
74314  }
74315 
74316  /* We're done for this chunk. */
74317  totalFramesProcessedIn += framesJustProcessedIn;
74318  totalFramesProcessedOut += framesJustProcessedOut;
74319 
74320  /* 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. */
74321  if (framesJustProcessedOut == 0) {
74322  break;
74323  }
74324  }
74325 
74326  /* At this point we're done processing. */
74327  *pFrameCountIn = totalFramesProcessedIn;
74328  *pFrameCountOut = totalFramesProcessedOut;
74329 }
74330 
74331 static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
74332 {
74333  /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */
74334  ma_result result = MA_SUCCESS;
74335  ma_sound* pSound = (ma_sound*)pNode;
74336  ma_uint32 frameCount = *pFrameCountOut;
74337  ma_uint32 totalFramesRead = 0;
74338  ma_format dataSourceFormat;
74339  ma_uint32 dataSourceChannels;
74340  ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
74341  ma_uint32 tempCapInFrames;
74342  ma_uint64 seekTarget;
74343 
74344  /* This is a data source node which means no input buses. */
74345  (void)ppFramesIn;
74346  (void)pFrameCountIn;
74347 
74348  /* If we're marked at the end we need to stop the sound and do nothing. */
74349  if (ma_sound_at_end(pSound)) {
74350  ma_sound_stop(pSound);
74351  *pFrameCountOut = 0;
74352  return;
74353  }
74354 
74355  /* If we're seeking, do so now before reading. */
74356  seekTarget = ma_atomic_load_64(&pSound->seekTarget);
74357  if (seekTarget != MA_SEEK_TARGET_NONE) {
74358  ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget);
74359 
74360  /* Any time-dependant effects need to have their times updated. */
74361  ma_node_set_time(pSound, seekTarget);
74362 
74363  ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE);
74364  }
74365 
74366  /*
74367  We want to update the pitch once. For sounds, this can be either at the start or at the end. If
74368  we don't force this to only ever be updating once, we could end up in a situation where
74369  retrieving the required input frame count ends up being different to what we actually retrieve.
74370  What could happen is that the required input frame count is calculated, the pitch is update,
74371  and then this processing function is called resulting in a different number of input frames
74372  being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else
74373  you'll hit the aforementioned bug.
74374  */
74375  ma_engine_node_update_pitch_if_required(&pSound->engineNode);
74376 
74377  /*
74378  For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ
74379  from the main engine.
74380  */
74381  result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0);
74382  if (result == MA_SUCCESS) {
74383  tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
74384 
74385  /* Keep reading until we've read as much as was requested or we reach the end of the data source. */
74386  while (totalFramesRead < frameCount) {
74387  ma_uint32 framesRemaining = frameCount - totalFramesRead;
74388  ma_uint32 framesToRead;
74389  ma_uint64 framesJustRead;
74390  ma_uint32 frameCountIn;
74391  ma_uint32 frameCountOut;
74392  const float* pRunningFramesIn;
74393  float* pRunningFramesOut;
74394 
74395  /*
74396  The first thing we need to do is read into the temporary buffer. We can calculate exactly
74397  how many input frames we'll need after resampling.
74398  */
74399  framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining);
74400  if (framesToRead > tempCapInFrames) {
74401  framesToRead = tempCapInFrames;
74402  }
74403 
74404  result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead);
74405 
74406  /* 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. */
74407  if (result == MA_AT_END) {
74408  ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */
74409  }
74410 
74411  pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound)));
74412 
74413  frameCountIn = (ma_uint32)framesJustRead;
74414  frameCountOut = framesRemaining;
74415 
74416  /* Convert if necessary. */
74417  if (dataSourceFormat == ma_format_f32) {
74418  /* Fast path. No data conversion necessary. */
74419  pRunningFramesIn = (float*)temp;
74420  ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
74421  } else {
74422  /* 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. */
74423  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. */
74424  ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
74425 
74426  /* Now that we have our samples in f32 format we can process like normal. */
74427  pRunningFramesIn = tempf32;
74428  ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
74429  }
74430 
74431  /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */
74432  MA_ASSERT(frameCountIn == framesJustRead);
74433  totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */
74434 
74435  if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
74436  break; /* Might have reached the end. */
74437  }
74438  }
74439  }
74440 
74441  *pFrameCountOut = totalFramesRead;
74442 }
74443 
74444 static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
74445 {
74446  /*
74447  Make sure the pitch is updated before trying to read anything. It's important that this is done
74448  only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that
74449  ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(),
74450  and if another thread modifies the pitch just after that call it can result in a glitch due to
74451  the input rate changing.
74452  */
74453  ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
74454 
74455  /* For groups, the input data has already been read and we just need to apply the effect. */
74456  ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
74457 }
74458 
74459 static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount)
74460 {
74461  ma_uint64 inputFrameCount;
74462 
74463  MA_ASSERT(pInputFrameCount != NULL);
74464 
74465  /* Our pitch will affect this calculation. We need to update it. */
74466  ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
74467 
74468  inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
74469  if (inputFrameCount > 0xFFFFFFFF) {
74470  inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
74471  }
74472 
74473  *pInputFrameCount = (ma_uint32)inputFrameCount;
74474 
74475  return MA_SUCCESS;
74476 }
74477 
74478 
74479 static ma_node_vtable g_ma_engine_node_vtable__sound =
74480 {
74481  ma_engine_node_process_pcm_frames__sound,
74482  NULL, /* onGetRequiredInputFrameCount */
74483  0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */
74484  1, /* Sounds have one output bus. */
74485  0 /* Default flags. */
74486 };
74487 
74488 static ma_node_vtable g_ma_engine_node_vtable__group =
74489 {
74490  ma_engine_node_process_pcm_frames__group,
74491  ma_engine_node_get_required_input_frame_count__group,
74492  1, /* Groups have one input bus. */
74493  1, /* Groups have one output bus. */
74494  MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */
74495 };
74496 
74497 
74498 
74499 static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig)
74500 {
74501  ma_node_config baseNodeConfig;
74502 
74503  if (pConfig->type == ma_engine_node_type_sound) {
74504  /* Sound. */
74505  baseNodeConfig = ma_node_config_init();
74506  baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound;
74507  baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */
74508  } else {
74509  /* Group. */
74510  baseNodeConfig = ma_node_config_init();
74511  baseNodeConfig.vtable = &g_ma_engine_node_vtable__group;
74512  baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */
74513  }
74514 
74515  return baseNodeConfig;
74516 }
74517 
74518 static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig)
74519 {
74520  return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]);
74521 }
74522 
74523 typedef struct
74524 {
74525  size_t sizeInBytes;
74526  size_t baseNodeOffset;
74527  size_t resamplerOffset;
74528  size_t spatializerOffset;
74529  size_t gainerOffset;
74530 } ma_engine_node_heap_layout;
74531 
74532 static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout)
74533 {
74534  ma_result result;
74535  size_t tempHeapSize;
74536  ma_node_config baseNodeConfig;
74537  ma_linear_resampler_config resamplerConfig;
74538  ma_spatializer_config spatializerConfig;
74539  ma_gainer_config gainerConfig;
74540  ma_uint32 channelsIn;
74541  ma_uint32 channelsOut;
74542  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. */
74543 
74544  MA_ASSERT(pHeapLayout);
74545 
74546  MA_ZERO_OBJECT(pHeapLayout);
74547 
74548  if (pConfig == NULL) {
74549  return MA_INVALID_ARGS;
74550  }
74551 
74552  if (pConfig->pEngine == NULL) {
74553  return MA_INVALID_ARGS; /* An engine must be specified. */
74554  }
74555 
74556  pHeapLayout->sizeInBytes = 0;
74557 
74558  channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
74559  channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
74560 
74561 
74562  /* Base node. */
74563  baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
74564  baseNodeConfig.pInputChannels = &channelsIn;
74565  baseNodeConfig.pOutputChannels = &channelsOut;
74566 
74567  result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize);
74568  if (result != MA_SUCCESS) {
74569  return result; /* Failed to retrieve the size of the heap for the base node. */
74570  }
74571 
74572  pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes;
74573  pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
74574 
74575 
74576  /* Resmapler. */
74577  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. */
74578  resamplerConfig.lpfOrder = 0;
74579 
74580  result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize);
74581  if (result != MA_SUCCESS) {
74582  return result; /* Failed to retrieve the size of the heap for the resampler. */
74583  }
74584 
74585  pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
74586  pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
74587 
74588 
74589  /* Spatializer. */
74590  spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
74591 
74592  if (spatializerConfig.channelsIn == 2) {
74593  spatializerConfig.pChannelMapIn = defaultStereoChannelMap;
74594  }
74595 
74596  result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize);
74597  if (result != MA_SUCCESS) {
74598  return result; /* Failed to retrieve the size of the heap for the spatializer. */
74599  }
74600 
74601  pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes;
74602  pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
74603 
74604 
74605  /* Gainer. Will not be used if we are not using smoothing. */
74606  if (pConfig->volumeSmoothTimeInPCMFrames > 0) {
74607  gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);
74608 
74609  result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize);
74610  if (result != MA_SUCCESS) {
74611  return result;
74612  }
74613 
74614  pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
74615  pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
74616  }
74617 
74618 
74619  return MA_SUCCESS;
74620 }
74621 
74622 MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes)
74623 {
74624  ma_result result;
74625  ma_engine_node_heap_layout heapLayout;
74626 
74627  if (pHeapSizeInBytes == NULL) {
74628  return MA_INVALID_ARGS;
74629  }
74630 
74631  *pHeapSizeInBytes = 0;
74632 
74633  result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
74634  if (result != MA_SUCCESS) {
74635  return result;
74636  }
74637 
74638  *pHeapSizeInBytes = heapLayout.sizeInBytes;
74639 
74640  return MA_SUCCESS;
74641 }
74642 
74643 MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode)
74644 {
74645  ma_result result;
74646  ma_engine_node_heap_layout heapLayout;
74647  ma_node_config baseNodeConfig;
74648  ma_linear_resampler_config resamplerConfig;
74649  ma_fader_config faderConfig;
74650  ma_spatializer_config spatializerConfig;
74651  ma_panner_config pannerConfig;
74652  ma_gainer_config gainerConfig;
74653  ma_uint32 channelsIn;
74654  ma_uint32 channelsOut;
74655  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. */
74656 
74657  if (pEngineNode == NULL) {
74658  return MA_INVALID_ARGS;
74659  }
74660 
74661  MA_ZERO_OBJECT(pEngineNode);
74662 
74663  result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
74664  if (result != MA_SUCCESS) {
74665  return result;
74666  }
74667 
74668  if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) {
74669  return MA_INVALID_ARGS; /* Invalid listener. */
74670  }
74671 
74672  pEngineNode->_pHeap = pHeap;
74673  MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
74674 
74675  pEngineNode->pEngine = pConfig->pEngine;
74676  pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine);
74677  pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;
74678  pEngineNode->monoExpansionMode = pConfig->monoExpansionMode;
74679  ma_atomic_float_set(&pEngineNode->volume, 1);
74680  pEngineNode->pitch = 1;
74681  pEngineNode->oldPitch = 1;
74682  pEngineNode->oldDopplerPitch = 1;
74683  pEngineNode->isPitchDisabled = pConfig->isPitchDisabled;
74684  pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled;
74685  pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex;
74686  ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1);
74687  ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1);
74688  ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0));
74689  ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */
74690 
74691  channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
74692  channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
74693 
74694  /*
74695  If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler
74696  is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used.
74697  */
74698  if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) {
74699  pEngineNode->isPitchDisabled = MA_FALSE;
74700  }
74701 
74702 
74703  /* Base node. */
74704  baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
74705  baseNodeConfig.pInputChannels = &channelsIn;
74706  baseNodeConfig.pOutputChannels = &channelsOut;
74707 
74708  result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode);
74709  if (result != MA_SUCCESS) {
74710  goto error0;
74711  }
74712 
74713 
74714  /*
74715  We can now initialize the effects we need in order to implement the engine node. There's a
74716  defined order of operations here, mainly centered around when we convert our channels from the
74717  data source's native channel count to the engine's channel count. As a rule, we want to do as
74718  much computation as possible before spatialization because there's a chance that will increase
74719  the channel count, thereby increasing the amount of work needing to be done to process.
74720  */
74721 
74722  /* We'll always do resampling first. */
74723  resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine));
74724  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. */
74725 
74726  result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler);
74727  if (result != MA_SUCCESS) {
74728  goto error1;
74729  }
74730 
74731 
74732  /* After resampling will come the fader. */
74733  faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine));
74734 
74735  result = ma_fader_init(&faderConfig, &pEngineNode->fader);
74736  if (result != MA_SUCCESS) {
74737  goto error2;
74738  }
74739 
74740 
74741  /*
74742  Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to
74743  ensure channels counts link up correctly in the node graph.
74744  */
74745  spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
74746  spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames;
74747 
74748  if (spatializerConfig.channelsIn == 2) {
74749  spatializerConfig.pChannelMapIn = defaultStereoChannelMap;
74750  }
74751 
74752  result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer);
74753  if (result != MA_SUCCESS) {
74754  goto error2;
74755  }
74756 
74757 
74758  /*
74759  After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't
74760  be able to pan mono sounds.
74761  */
74762  pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]);
74763 
74764  result = ma_panner_init(&pannerConfig, &pEngineNode->panner);
74765  if (result != MA_SUCCESS) {
74766  goto error3;
74767  }
74768 
74769 
74770  /* 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. */
74771  if (pConfig->volumeSmoothTimeInPCMFrames > 0) {
74772  gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);
74773 
74774  result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer);
74775  if (result != MA_SUCCESS) {
74776  goto error3;
74777  }
74778  }
74779 
74780 
74781  return MA_SUCCESS;
74782 
74783  /* No need for allocation callbacks here because we use a preallocated heap. */
74784 error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL);
74785 error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL);
74786 error1: ma_node_uninit(&pEngineNode->baseNode, NULL);
74787 error0: return result;
74788 }
74789 
74790 MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)
74791 {
74792  ma_result result;
74793  size_t heapSizeInBytes;
74794  void* pHeap;
74795 
74796  result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes);
74797  if (result != MA_SUCCESS) {
74798  return result;
74799  }
74800 
74801  if (heapSizeInBytes > 0) {
74802  pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
74803  if (pHeap == NULL) {
74804  return MA_OUT_OF_MEMORY;
74805  }
74806  } else {
74807  pHeap = NULL;
74808  }
74809 
74810  result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode);
74811  if (result != MA_SUCCESS) {
74812  ma_free(pHeap, pAllocationCallbacks);
74813  return result;
74814  }
74815 
74816  pEngineNode->_ownsHeap = MA_TRUE;
74817  return MA_SUCCESS;
74818 }
74819 
74820 MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks)
74821 {
74822  /*
74823  The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we
74824  destroy anything that might be in the middle of being used by the processing function.
74825  */
74826  ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks);
74827 
74828  /* Now that the node has been uninitialized we can safely uninitialize the rest. */
74829  if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) {
74830  ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks);
74831  }
74832 
74833  ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks);
74834  ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks);
74835 
74836  /* Free the heap last. */
74837  if (pEngineNode->_ownsHeap) {
74838  ma_free(pEngineNode->_pHeap, pAllocationCallbacks);
74839  }
74840 }
74841 
74842 
74843 MA_API ma_sound_config ma_sound_config_init(void)
74844 {
74845  return ma_sound_config_init_2(NULL);
74846 }
74847 
74848 MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine)
74849 {
74850  ma_sound_config config;
74851 
74852  MA_ZERO_OBJECT(&config);
74853 
74854  if (pEngine != NULL) {
74855  config.monoExpansionMode = pEngine->monoExpansionMode;
74856  } else {
74857  config.monoExpansionMode = ma_mono_expansion_mode_default;
74858  }
74859 
74860  config.rangeEndInPCMFrames = ~((ma_uint64)0);
74861  config.loopPointEndInPCMFrames = ~((ma_uint64)0);
74862 
74863  return config;
74864 }
74865 
74866 MA_API ma_sound_group_config ma_sound_group_config_init(void)
74867 {
74868  return ma_sound_group_config_init_2(NULL);
74869 }
74870 
74871 MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine)
74872 {
74873  ma_sound_group_config config;
74874 
74875  MA_ZERO_OBJECT(&config);
74876 
74877  if (pEngine != NULL) {
74878  config.monoExpansionMode = pEngine->monoExpansionMode;
74879  } else {
74880  config.monoExpansionMode = ma_mono_expansion_mode_default;
74881  }
74882 
74883  return config;
74884 }
74885 
74886 
74887 MA_API ma_engine_config ma_engine_config_init(void)
74888 {
74889  ma_engine_config config;
74890 
74891  MA_ZERO_OBJECT(&config);
74892  config.listenerCount = 1; /* Always want at least one listener. */
74893  config.monoExpansionMode = ma_mono_expansion_mode_default;
74894 
74895  return config;
74896 }
74897 
74898 
74899 #if !defined(MA_NO_DEVICE_IO)
74900 static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
74901 {
74902  ma_engine* pEngine = (ma_engine*)pDevice->pUserData;
74903 
74904  (void)pFramesIn;
74905 
74906  /*
74907  Experiment: Try processing a resource manager job if we're on the Emscripten build.
74908 
74909  This serves two purposes:
74910 
74911  1) It ensures jobs are actually processed at some point since we cannot guarantee that the
74912  caller is doing the right thing and calling ma_resource_manager_process_next_job(); and
74913 
74914  2) It's an attempt at working around an issue where processing jobs on the Emscripten main
74915  loop doesn't work as well as it should. When trying to load sounds without the `DECODE`
74916  flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time
74917  before the callback is processed. I think it's got something to do with the single-
74918  threaded nature of Web, but I'm not entirely sure.
74919  */
74920  #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN)
74921  {
74922  if (pEngine->pResourceManager != NULL) {
74923  if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
74924  ma_resource_manager_process_next_job(pEngine->pResourceManager);
74925  }
74926  }
74927  }
74928  #endif
74929 
74930  ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL);
74931 }
74932 #endif
74933 
74934 MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine)
74935 {
74936  ma_result result;
74937  ma_node_graph_config nodeGraphConfig;
74938  ma_engine_config engineConfig;
74939  ma_spatializer_listener_config listenerConfig;
74940  ma_uint32 iListener;
74941 
74942  if (pEngine == NULL) {
74943  return MA_INVALID_ARGS;
74944  }
74945 
74946  MA_ZERO_OBJECT(pEngine);
74947 
74948  /* The config is allowed to be NULL in which case we use defaults for everything. */
74949  if (pConfig != NULL) {
74950  engineConfig = *pConfig;
74951  } else {
74952  engineConfig = ma_engine_config_init();
74953  }
74954 
74955  pEngine->monoExpansionMode = engineConfig.monoExpansionMode;
74956  pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames;
74957  pEngine->onProcess = engineConfig.onProcess;
74958  pEngine->pProcessUserData = engineConfig.pProcessUserData;
74959  ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks);
74960 
74961  #if !defined(MA_NO_RESOURCE_MANAGER)
74962  {
74963  pEngine->pResourceManager = engineConfig.pResourceManager;
74964  }
74965  #endif
74966 
74967  #if !defined(MA_NO_DEVICE_IO)
74968  {
74969  pEngine->pDevice = engineConfig.pDevice;
74970 
74971  /* If we don't have a device, we need one. */
74972  if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) {
74973  ma_device_config deviceConfig;
74974 
74975  pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks);
74976  if (pEngine->pDevice == NULL) {
74977  return MA_OUT_OF_MEMORY;
74978  }
74979 
74980  deviceConfig = ma_device_config_init(ma_device_type_playback);
74981  deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID;
74982  deviceConfig.playback.format = ma_format_f32;
74983  deviceConfig.playback.channels = engineConfig.channels;
74984  deviceConfig.sampleRate = engineConfig.sampleRate;
74985  deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal;
74986  deviceConfig.pUserData = pEngine;
74987  deviceConfig.notificationCallback = engineConfig.notificationCallback;
74988  deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames;
74989  deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds;
74990  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. */
74991  deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */
74992 
74993  if (engineConfig.pContext == NULL) {
74994  ma_context_config contextConfig = ma_context_config_init();
74995  contextConfig.allocationCallbacks = pEngine->allocationCallbacks;
74996  contextConfig.pLog = engineConfig.pLog;
74997 
74998  /* If the engine config does not specify a log, use the resource manager's if we have one. */
74999  #ifndef MA_NO_RESOURCE_MANAGER
75000  {
75001  if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) {
75002  contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager);
75003  }
75004  }
75005  #endif
75006 
75007  result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice);
75008  } else {
75009  result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice);
75010  }
75011 
75012  if (result != MA_SUCCESS) {
75013  ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
75014  pEngine->pDevice = NULL;
75015  return result;
75016  }
75017 
75018  pEngine->ownsDevice = MA_TRUE;
75019  }
75020 
75021  /* Update the channel count and sample rate of the engine config so we can reference it below. */
75022  if (pEngine->pDevice != NULL) {
75023  engineConfig.channels = pEngine->pDevice->playback.channels;
75024  engineConfig.sampleRate = pEngine->pDevice->sampleRate;
75025  }
75026  }
75027  #endif
75028 
75029  if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) {
75030  return MA_INVALID_ARGS;
75031  }
75032 
75033  pEngine->sampleRate = engineConfig.sampleRate;
75034 
75035  /* The engine always uses either the log that was passed into the config, or the context's log is available. */
75036  if (engineConfig.pLog != NULL) {
75037  pEngine->pLog = engineConfig.pLog;
75038  } else {
75039  #if !defined(MA_NO_DEVICE_IO)
75040  {
75041  pEngine->pLog = ma_device_get_log(pEngine->pDevice);
75042  }
75043  #else
75044  {
75045  pEngine->pLog = NULL;
75046  }
75047  #endif
75048  }
75049 
75050 
75051  /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */
75052  nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels);
75053  nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames;
75054 
75055  result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph);
75056  if (result != MA_SUCCESS) {
75057  goto on_error_1;
75058  }
75059 
75060 
75061  /* We need at least one listener. */
75062  if (engineConfig.listenerCount == 0) {
75063  engineConfig.listenerCount = 1;
75064  }
75065 
75066  if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) {
75067  result = MA_INVALID_ARGS; /* Too many listeners. */
75068  goto on_error_1;
75069  }
75070 
75071  for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) {
75072  listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph));
75073 
75074  /*
75075  If we're using a device, use the device's channel map for the listener. Otherwise just use
75076  miniaudio's default channel map.
75077  */
75078  #if !defined(MA_NO_DEVICE_IO)
75079  {
75080  if (pEngine->pDevice != NULL) {
75081  /*
75082  Temporarily disabled. There is a subtle bug here where front-left and front-right
75083  will be used by the device's channel map, but this is not what we want to use for
75084  spatialization. Instead we want to use side-left and side-right. I need to figure
75085  out a better solution for this. For now, disabling the use of device channel maps.
75086  */
75087  /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/
75088  }
75089  }
75090  #endif
75091 
75092  result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */
75093  if (result != MA_SUCCESS) {
75094  goto on_error_2;
75095  }
75096 
75097  pEngine->listenerCount += 1;
75098  }
75099 
75100 
75101  /* Gain smoothing for spatialized sounds. */
75102  pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames;
75103  if (pEngine->gainSmoothTimeInFrames == 0) {
75104  ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds;
75105  if (gainSmoothTimeInMilliseconds == 0) {
75106  gainSmoothTimeInMilliseconds = 8;
75107  }
75108 
75109  pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */
75110  }
75111 
75112 
75113  /* We need a resource manager. */
75114  #ifndef MA_NO_RESOURCE_MANAGER
75115  {
75116  if (pEngine->pResourceManager == NULL) {
75117  ma_resource_manager_config resourceManagerConfig;
75118 
75119  pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks);
75120  if (pEngine->pResourceManager == NULL) {
75121  result = MA_OUT_OF_MEMORY;
75122  goto on_error_2;
75123  }
75124 
75125  resourceManagerConfig = ma_resource_manager_config_init();
75126  resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */
75127  resourceManagerConfig.decodedFormat = ma_format_f32;
75128  resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */
75129  resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine);
75130  ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks);
75131  resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS;
75132 
75133  /* The Emscripten build cannot use threads. */
75134  #if defined(MA_EMSCRIPTEN)
75135  {
75136  resourceManagerConfig.jobThreadCount = 0;
75137  resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
75138  }
75139  #endif
75140 
75141  result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager);
75142  if (result != MA_SUCCESS) {
75143  goto on_error_3;
75144  }
75145 
75146  pEngine->ownsResourceManager = MA_TRUE;
75147  }
75148  }
75149  #endif
75150 
75151  /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */
75152  pEngine->inlinedSoundLock = 0;
75153  pEngine->pInlinedSoundHead = NULL;
75154 
75155  /* Start the engine if required. This should always be the last step. */
75156  #if !defined(MA_NO_DEVICE_IO)
75157  {
75158  if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) {
75159  result = ma_engine_start(pEngine);
75160  if (result != MA_SUCCESS) {
75161  goto on_error_4; /* Failed to start the engine. */
75162  }
75163  }
75164  }
75165  #endif
75166 
75167  return MA_SUCCESS;
75168 
75169 #if !defined(MA_NO_DEVICE_IO)
75170 on_error_4:
75171 #endif
75172 #if !defined(MA_NO_RESOURCE_MANAGER)
75173 on_error_3:
75174  if (pEngine->ownsResourceManager) {
75175  ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
75176  }
75177 #endif /* MA_NO_RESOURCE_MANAGER */
75178 on_error_2:
75179  for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
75180  ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
75181  }
75182 
75183  ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
75184 on_error_1:
75185  #if !defined(MA_NO_DEVICE_IO)
75186  {
75187  if (pEngine->ownsDevice) {
75188  ma_device_uninit(pEngine->pDevice);
75189  ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
75190  }
75191  }
75192  #endif
75193 
75194  return result;
75195 }
75196 
75197 MA_API void ma_engine_uninit(ma_engine* pEngine)
75198 {
75199  ma_uint32 iListener;
75200 
75201  if (pEngine == NULL) {
75202  return;
75203  }
75204 
75205  /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */
75206  #if !defined(MA_NO_DEVICE_IO)
75207  {
75208  if (pEngine->ownsDevice) {
75209  ma_device_uninit(pEngine->pDevice);
75210  ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
75211  } else {
75212  if (pEngine->pDevice != NULL) {
75213  ma_device_stop(pEngine->pDevice);
75214  }
75215  }
75216  }
75217  #endif
75218 
75219  /*
75220  All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case
75221  I want to do some kind of garbage collection later on.
75222  */
75223  ma_spinlock_lock(&pEngine->inlinedSoundLock);
75224  {
75225  for (;;) {
75226  ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead;
75227  if (pSoundToDelete == NULL) {
75228  break; /* Done. */
75229  }
75230 
75231  pEngine->pInlinedSoundHead = pSoundToDelete->pNext;
75232 
75233  ma_sound_uninit(&pSoundToDelete->sound);
75234  ma_free(pSoundToDelete, &pEngine->allocationCallbacks);
75235  }
75236  }
75237  ma_spinlock_unlock(&pEngine->inlinedSoundLock);
75238 
75239  for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
75240  ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
75241  }
75242 
75243  /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */
75244  ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
75245 
75246  /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */
75247 #ifndef MA_NO_RESOURCE_MANAGER
75248  if (pEngine->ownsResourceManager) {
75249  ma_resource_manager_uninit(pEngine->pResourceManager);
75250  ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
75251  }
75252 #endif
75253 }
75254 
75255 MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
75256 {
75257  ma_result result;
75258  ma_uint64 framesRead = 0;
75259 
75260  if (pFramesRead != NULL) {
75261  *pFramesRead = 0;
75262  }
75263 
75264  result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead);
75265  if (result != MA_SUCCESS) {
75266  return result;
75267  }
75268 
75269  if (pFramesRead != NULL) {
75270  *pFramesRead = framesRead;
75271  }
75272 
75273  if (pEngine->onProcess) {
75274  pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */
75275  }
75276 
75277  return MA_SUCCESS;
75278 }
75279 
75280 MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine)
75281 {
75282  if (pEngine == NULL) {
75283  return NULL;
75284  }
75285 
75286  return &pEngine->nodeGraph;
75287 }
75288 
75289 #if !defined(MA_NO_RESOURCE_MANAGER)
75290 MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine)
75291 {
75292  if (pEngine == NULL) {
75293  return NULL;
75294  }
75295 
75296  #if !defined(MA_NO_RESOURCE_MANAGER)
75297  {
75298  return pEngine->pResourceManager;
75299  }
75300  #else
75301  {
75302  return NULL;
75303  }
75304  #endif
75305 }
75306 #endif
75307 
75308 MA_API ma_device* ma_engine_get_device(ma_engine* pEngine)
75309 {
75310  if (pEngine == NULL) {
75311  return NULL;
75312  }
75313 
75314  #if !defined(MA_NO_DEVICE_IO)
75315  {
75316  return pEngine->pDevice;
75317  }
75318  #else
75319  {
75320  return NULL;
75321  }
75322  #endif
75323 }
75324 
75325 MA_API ma_log* ma_engine_get_log(ma_engine* pEngine)
75326 {
75327  if (pEngine == NULL) {
75328  return NULL;
75329  }
75330 
75331  if (pEngine->pLog != NULL) {
75332  return pEngine->pLog;
75333  } else {
75334  #if !defined(MA_NO_DEVICE_IO)
75335  {
75336  return ma_device_get_log(ma_engine_get_device(pEngine));
75337  }
75338  #else
75339  {
75340  return NULL;
75341  }
75342  #endif
75343  }
75344 }
75345 
75346 MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine)
75347 {
75348  return ma_node_graph_get_endpoint(&pEngine->nodeGraph);
75349 }
75350 
75351 MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine)
75352 {
75353  return ma_node_graph_get_time(&pEngine->nodeGraph);
75354 }
75355 
75356 MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine)
75357 {
75358  return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine);
75359 }
75360 
75361 MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime)
75362 {
75363  return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime);
75364 }
75365 
75366 MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime)
75367 {
75368  return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000);
75369 }
75370 
75371 MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine)
75372 {
75373  return ma_engine_get_time_in_pcm_frames(pEngine);
75374 }
75375 
75376 MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime)
75377 {
75378  return ma_engine_set_time_in_pcm_frames(pEngine, globalTime);
75379 }
75380 
75381 MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine)
75382 {
75383  return ma_node_graph_get_channels(&pEngine->nodeGraph);
75384 }
75385 
75386 MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine)
75387 {
75388  if (pEngine == NULL) {
75389  return 0;
75390  }
75391 
75392  return pEngine->sampleRate;
75393 }
75394 
75395 
75396 MA_API ma_result ma_engine_start(ma_engine* pEngine)
75397 {
75398  ma_result result;
75399 
75400  if (pEngine == NULL) {
75401  return MA_INVALID_ARGS;
75402  }
75403 
75404  #if !defined(MA_NO_DEVICE_IO)
75405  {
75406  if (pEngine->pDevice != NULL) {
75407  result = ma_device_start(pEngine->pDevice);
75408  } else {
75409  result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */
75410  }
75411  }
75412  #else
75413  {
75414  result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */
75415  }
75416  #endif
75417 
75418  if (result != MA_SUCCESS) {
75419  return result;
75420  }
75421 
75422  return MA_SUCCESS;
75423 }
75424 
75425 MA_API ma_result ma_engine_stop(ma_engine* pEngine)
75426 {
75427  ma_result result;
75428 
75429  if (pEngine == NULL) {
75430  return MA_INVALID_ARGS;
75431  }
75432 
75433  #if !defined(MA_NO_DEVICE_IO)
75434  {
75435  if (pEngine->pDevice != NULL) {
75436  result = ma_device_stop(pEngine->pDevice);
75437  } else {
75438  result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */
75439  }
75440  }
75441  #else
75442  {
75443  result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */
75444  }
75445  #endif
75446 
75447  if (result != MA_SUCCESS) {
75448  return result;
75449  }
75450 
75451  return MA_SUCCESS;
75452 }
75453 
75454 MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume)
75455 {
75456  if (pEngine == NULL) {
75457  return MA_INVALID_ARGS;
75458  }
75459 
75460  return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume);
75461 }
75462 
75463 MA_API float ma_engine_get_volume(ma_engine* pEngine)
75464 {
75465  if (pEngine == NULL) {
75466  return 0;
75467  }
75468 
75469  return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);
75470 }
75471 
75472 MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB)
75473 {
75474  return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB));
75475 }
75476 
75477 MA_API float ma_engine_get_gain_db(ma_engine* pEngine)
75478 {
75479  return ma_volume_linear_to_db(ma_engine_get_volume(pEngine));
75480 }
75481 
75482 
75483 MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine)
75484 {
75485  if (pEngine == NULL) {
75486  return 0;
75487  }
75488 
75489  return pEngine->listenerCount;
75490 }
75491 
75492 MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)
75493 {
75494  ma_uint32 iListener;
75495  ma_uint32 iListenerClosest;
75496  float closestLen2 = MA_FLT_MAX;
75497 
75498  if (pEngine == NULL || pEngine->listenerCount == 1) {
75499  return 0;
75500  }
75501 
75502  iListenerClosest = 0;
75503  for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
75504  if (ma_engine_listener_is_enabled(pEngine, iListener)) {
75505  float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ)));
75506  if (closestLen2 > len2) {
75507  closestLen2 = len2;
75508  iListenerClosest = iListener;
75509  }
75510  }
75511  }
75512 
75513  MA_ASSERT(iListenerClosest < 255);
75514  return iListenerClosest;
75515 }
75516 
75517 MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
75518 {
75519  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75520  return;
75521  }
75522 
75523  ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z);
75524 }
75525 
75526 MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex)
75527 {
75528  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75529  return ma_vec3f_init_3f(0, 0, 0);
75530  }
75531 
75532  return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]);
75533 }
75534 
75535 MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
75536 {
75537  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75538  return;
75539  }
75540 
75541  ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z);
75542 }
75543 
75544 MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex)
75545 {
75546  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75547  return ma_vec3f_init_3f(0, 0, -1);
75548  }
75549 
75550  return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]);
75551 }
75552 
75553 MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
75554 {
75555  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75556  return;
75557  }
75558 
75559  ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z);
75560 }
75561 
75562 MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex)
75563 {
75564  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75565  return ma_vec3f_init_3f(0, 0, 0);
75566  }
75567 
75568  return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]);
75569 }
75570 
75571 MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
75572 {
75573  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75574  return;
75575  }
75576 
75577  ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain);
75578 }
75579 
75580 MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
75581 {
75582  if (pInnerAngleInRadians != NULL) {
75583  *pInnerAngleInRadians = 0;
75584  }
75585 
75586  if (pOuterAngleInRadians != NULL) {
75587  *pOuterAngleInRadians = 0;
75588  }
75589 
75590  if (pOuterGain != NULL) {
75591  *pOuterGain = 0;
75592  }
75593 
75594  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75595  return;
75596  }
75597 
75598  ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
75599 }
75600 
75601 MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
75602 {
75603  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75604  return;
75605  }
75606 
75607  ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z);
75608 }
75609 
75610 MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex)
75611 {
75612  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75613  return ma_vec3f_init_3f(0, 1, 0);
75614  }
75615 
75616  return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]);
75617 }
75618 
75619 MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled)
75620 {
75621  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75622  return;
75623  }
75624 
75625  ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled);
75626 }
75627 
75628 MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex)
75629 {
75630  if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75631  return MA_FALSE;
75632  }
75633 
75634  return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]);
75635 }
75636 
75637 
75638 #ifndef MA_NO_RESOURCE_MANAGER
75639 MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex)
75640 {
75641  ma_result result = MA_SUCCESS;
75642  ma_sound_inlined* pSound = NULL;
75643  ma_sound_inlined* pNextSound = NULL;
75644 
75645  if (pEngine == NULL || pFilePath == NULL) {
75646  return MA_INVALID_ARGS;
75647  }
75648 
75649  /* Attach to the endpoint node if nothing is specicied. */
75650  if (pNode == NULL) {
75651  pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph);
75652  nodeInputBusIndex = 0;
75653  }
75654 
75655  /*
75656  We want to check if we can recycle an already-allocated inlined sound. Since this is just a
75657  helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep
75658  the implementation simple. Maybe this can be optimized later if there's enough demand, but
75659  if this function is being used it probably means the caller doesn't really care too much.
75660 
75661  What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise
75662  we just keep iterating. If we reach the end without finding a sound to recycle we just
75663  allocate a new one. This doesn't scale well for a massive number of sounds being played
75664  simultaneously as we don't ever actually free the sound objects. Some kind of garbage
75665  collection routine might be valuable for this which I'll think about.
75666  */
75667  ma_spinlock_lock(&pEngine->inlinedSoundLock);
75668  {
75669  ma_uint32 soundFlags = 0;
75670 
75671  for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) {
75672  if (ma_sound_at_end(&pNextSound->sound)) {
75673  /*
75674  The sound is at the end which means it's available for recycling. All we need to do
75675  is uninitialize it and reinitialize it. All we're doing is recycling memory.
75676  */
75677  pSound = pNextSound;
75678  ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1);
75679  break;
75680  }
75681  }
75682 
75683  if (pSound != NULL) {
75684  /*
75685  We actually want to detach the sound from the list here. The reason is because we want the sound
75686  to be in a consistent state at the non-recycled case to simplify the logic below.
75687  */
75688  if (pEngine->pInlinedSoundHead == pSound) {
75689  pEngine->pInlinedSoundHead = pSound->pNext;
75690  }
75691 
75692  if (pSound->pPrev != NULL) {
75693  pSound->pPrev->pNext = pSound->pNext;
75694  }
75695  if (pSound->pNext != NULL) {
75696  pSound->pNext->pPrev = pSound->pPrev;
75697  }
75698 
75699  /* Now the previous sound needs to be uninitialized. */
75700  ma_sound_uninit(&pNextSound->sound);
75701  } else {
75702  /* No sound available for recycling. Allocate one now. */
75703  pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks);
75704  }
75705 
75706  if (pSound != NULL) { /* Safety check for the allocation above. */
75707  /*
75708  At this point we should have memory allocated for the inlined sound. We just need
75709  to initialize it like a normal sound now.
75710  */
75711  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. */
75712  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. */
75713  soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */
75714  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. */
75715 
75716  result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound);
75717  if (result == MA_SUCCESS) {
75718  /* Now attach the sound to the graph. */
75719  result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex);
75720  if (result == MA_SUCCESS) {
75721  /* 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. */
75722  pSound->pNext = pEngine->pInlinedSoundHead;
75723  pSound->pPrev = NULL;
75724 
75725  pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */
75726  if (pSound->pNext != NULL) {
75727  pSound->pNext->pPrev = pSound;
75728  }
75729  } else {
75730  ma_free(pSound, &pEngine->allocationCallbacks);
75731  }
75732  } else {
75733  ma_free(pSound, &pEngine->allocationCallbacks);
75734  }
75735  } else {
75736  result = MA_OUT_OF_MEMORY;
75737  }
75738  }
75739  ma_spinlock_unlock(&pEngine->inlinedSoundLock);
75740 
75741  if (result != MA_SUCCESS) {
75742  return result;
75743  }
75744 
75745  /* Finally we can start playing the sound. */
75746  result = ma_sound_start(&pSound->sound);
75747  if (result != MA_SUCCESS) {
75748  /* Failed to start the sound. We need to mark it for recycling and return an error. */
75749  ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE);
75750  return result;
75751  }
75752 
75753  ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1);
75754  return result;
75755 }
75756 
75757 MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)
75758 {
75759  return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0);
75760 }
75761 #endif
75762 
75763 
75764 static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound)
75765 {
75766  if (pSound == NULL) {
75767  return MA_INVALID_ARGS;
75768  }
75769 
75770  MA_ZERO_OBJECT(pSound);
75771  pSound->seekTarget = MA_SEEK_TARGET_NONE;
75772 
75773  if (pEngine == NULL) {
75774  return MA_INVALID_ARGS;
75775  }
75776 
75777  return MA_SUCCESS;
75778 }
75779 
75780 static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
75781 {
75782  ma_result result;
75783  ma_engine_node_config engineNodeConfig;
75784  ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */
75785 
75786  /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */
75787  MA_ASSERT(pEngine != NULL);
75788  MA_ASSERT(pSound != NULL);
75789 
75790  if (pConfig == NULL) {
75791  return MA_INVALID_ARGS;
75792  }
75793 
75794  pSound->pDataSource = pConfig->pDataSource;
75795 
75796  if (pConfig->pDataSource != NULL) {
75797  type = ma_engine_node_type_sound;
75798  } else {
75799  type = ma_engine_node_type_group;
75800  }
75801 
75802  /*
75803  Sounds are engine nodes. Before we can initialize this we need to determine the channel count.
75804  If we can't do this we need to abort. It's up to the caller to ensure they're using a data
75805  source that provides this information upfront.
75806  */
75807  engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags);
75808  engineNodeConfig.channelsIn = pConfig->channelsIn;
75809  engineNodeConfig.channelsOut = pConfig->channelsOut;
75810  engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;
75811  engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode;
75812 
75813  if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) {
75814  engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames;
75815  }
75816 
75817  /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */
75818  if (pConfig->pDataSource != NULL) {
75819  result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0);
75820  if (result != MA_SUCCESS) {
75821  return result; /* Failed to retrieve the channel count. */
75822  }
75823 
75824  if (engineNodeConfig.channelsIn == 0) {
75825  return MA_INVALID_OPERATION; /* Invalid channel count. */
75826  }
75827 
75828  if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) {
75829  engineNodeConfig.channelsOut = engineNodeConfig.channelsIn;
75830  }
75831  }
75832 
75833 
75834  /* Getting here means we should have a valid channel count and we can initialize the engine node. */
75835  result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode);
75836  if (result != MA_SUCCESS) {
75837  return result;
75838  }
75839 
75840  /* If no attachment is specified, attach the sound straight to the endpoint. */
75841  if (pConfig->pInitialAttachment == NULL) {
75842  /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */
75843  if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) {
75844  result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);
75845  }
75846  } else {
75847  /* 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. */
75848  result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex);
75849  }
75850 
75851  if (result != MA_SUCCESS) {
75852  ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks);
75853  return result;
75854  }
75855 
75856 
75857  /* Apply initial range and looping state to the data source if applicable. */
75858  if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) {
75859  ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
75860  }
75861 
75862  if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) {
75863  ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
75864  }
75865 
75866  ma_sound_set_looping(pSound, pConfig->isLooping);
75867 
75868  return MA_SUCCESS;
75869 }
75870 
75871 #ifndef MA_NO_RESOURCE_MANAGER
75872 MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
75873 {
75874  ma_result result = MA_SUCCESS;
75875  ma_uint32 flags;
75876  ma_sound_config config;
75878 
75879  /*
75880  The engine requires knowledge of the channel count of the underlying data source before it can
75881  initialize the sound. Therefore, we need to make the resource manager wait until initialization
75882  of the underlying data source to be initialized so we can get access to the channel count. To
75883  do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced.
75884 
75885  Because we're initializing the data source before the sound, there's a chance the notification
75886  will get triggered before this function returns. This is OK, so long as the caller is aware of
75887  it and can avoid accessing the sound from within the notification.
75888  */
75889  flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT;
75890 
75891  pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);
75892  if (pSound->pResourceManagerDataSource == NULL) {
75893  return MA_OUT_OF_MEMORY;
75894  }
75895 
75896  /* Removed in 0.12. Set pDoneFence on the notifications. */
75897  notifications = pConfig->initNotifications;
75898  if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) {
75899  notifications.done.pFence = pConfig->pDoneFence;
75900  }
75901 
75902  /*
75903  We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does
75904  not return prematurely before the sound has finished initializing.
75905  */
75906  if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); }
75907  {
75908  ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init();
75909  resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath;
75910  resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW;
75911  resourceManagerDataSourceConfig.flags = flags;
75912  resourceManagerDataSourceConfig.pNotifications = &notifications;
75913  resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames;
75914  resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
75915  resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
75916  resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
75917  resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
75918  resourceManagerDataSourceConfig.isLooping = pConfig->isLooping;
75919 
75920  result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource);
75921  if (result != MA_SUCCESS) {
75922  goto done;
75923  }
75924 
75925  pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */
75926 
75927  /* We need to use a slightly customized version of the config so we'll need to make a copy. */
75928  config = *pConfig;
75929  config.pFilePath = NULL;
75930  config.pFilePathW = NULL;
75931  config.pDataSource = pSound->pResourceManagerDataSource;
75932 
75933  result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
75934  if (result != MA_SUCCESS) {
75935  ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
75936  ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
75937  MA_ZERO_OBJECT(pSound);
75938  goto done;
75939  }
75940  }
75941 done:
75942  if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); }
75943  return result;
75944 }
75945 
75946 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)
75947 {
75948  ma_sound_config config;
75949 
75950  if (pFilePath == NULL) {
75951  return MA_INVALID_ARGS;
75952  }
75953 
75954  config = ma_sound_config_init_2(pEngine);
75955  config.pFilePath = pFilePath;
75956  config.flags = flags;
75957  config.pInitialAttachment = pGroup;
75958  config.pDoneFence = pDoneFence;
75959 
75960  return ma_sound_init_ex(pEngine, &config, pSound);
75961 }
75962 
75963 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)
75964 {
75965  ma_sound_config config;
75966 
75967  if (pFilePath == NULL) {
75968  return MA_INVALID_ARGS;
75969  }
75970 
75971  config = ma_sound_config_init_2(pEngine);
75972  config.pFilePathW = pFilePath;
75973  config.flags = flags;
75974  config.pInitialAttachment = pGroup;
75975  config.pDoneFence = pDoneFence;
75976 
75977  return ma_sound_init_ex(pEngine, &config, pSound);
75978 }
75979 
75980 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)
75981 {
75982  ma_result result;
75983  ma_sound_config config;
75984 
75985  result = ma_sound_preinit(pEngine, pSound);
75986  if (result != MA_SUCCESS) {
75987  return result;
75988  }
75989 
75990  if (pExistingSound == NULL) {
75991  return MA_INVALID_ARGS;
75992  }
75993 
75994  /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */
75995  if (pExistingSound->pResourceManagerDataSource == NULL) {
75996  return MA_INVALID_OPERATION;
75997  }
75998 
75999  /*
76000  We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream)
76001  this will fail.
76002  */
76003  pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);
76004  if (pSound->pResourceManagerDataSource == NULL) {
76005  return MA_OUT_OF_MEMORY;
76006  }
76007 
76008  result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource);
76009  if (result != MA_SUCCESS) {
76010  ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
76011  return result;
76012  }
76013 
76014  config = ma_sound_config_init_2(pEngine);
76015  config.pDataSource = pSound->pResourceManagerDataSource;
76016  config.flags = flags;
76017  config.pInitialAttachment = pGroup;
76018  config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode;
76019  config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames;
76020 
76021  result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
76022  if (result != MA_SUCCESS) {
76023  ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
76024  ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
76025  MA_ZERO_OBJECT(pSound);
76026  return result;
76027  }
76028 
76029  /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */
76030  pSound->ownsDataSource = MA_TRUE;
76031 
76032  return MA_SUCCESS;
76033 }
76034 #endif
76035 
76036 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)
76037 {
76038  ma_sound_config config = ma_sound_config_init_2(pEngine);
76039  config.pDataSource = pDataSource;
76040  config.flags = flags;
76041  config.pInitialAttachment = pGroup;
76042  return ma_sound_init_ex(pEngine, &config, pSound);
76043 }
76044 
76045 MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
76046 {
76047  ma_result result;
76048 
76049  result = ma_sound_preinit(pEngine, pSound);
76050  if (result != MA_SUCCESS) {
76051  return result;
76052  }
76053 
76054  if (pConfig == NULL) {
76055  return MA_INVALID_ARGS;
76056  }
76057 
76058  pSound->endCallback = pConfig->endCallback;
76059  pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData;
76060 
76061  /* We need to load the sound differently depending on whether or not we're loading from a file. */
76062 #ifndef MA_NO_RESOURCE_MANAGER
76063  if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) {
76064  return ma_sound_init_from_file_internal(pEngine, pConfig, pSound);
76065  } else
76066 #endif
76067  {
76068  /*
76069  Getting here means we're not loading from a file. We may be loading from an already-initialized
76070  data source, or none at all. If we aren't specifying any data source, we'll be initializing the
76071  the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this
76072  for us, so no special treatment required here.
76073  */
76074  return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound);
76075  }
76076 }
76077 
76078 MA_API void ma_sound_uninit(ma_sound* pSound)
76079 {
76080  if (pSound == NULL) {
76081  return;
76082  }
76083 
76084  /*
76085  Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done
76086  so which makes thread safety beyond this point trivial.
76087  */
76088  ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks);
76089 
76090  /* 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. */
76091 #ifndef MA_NO_RESOURCE_MANAGER
76092  if (pSound->ownsDataSource) {
76093  ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
76094  ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks);
76095  pSound->pDataSource = NULL;
76096  }
76097 #else
76098  MA_ASSERT(pSound->ownsDataSource == MA_FALSE);
76099 #endif
76100 }
76101 
76102 MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound)
76103 {
76104  if (pSound == NULL) {
76105  return NULL;
76106  }
76107 
76108  return pSound->engineNode.pEngine;
76109 }
76110 
76111 MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound)
76112 {
76113  if (pSound == NULL) {
76114  return NULL;
76115  }
76116 
76117  return pSound->pDataSource;
76118 }
76119 
76120 MA_API ma_result ma_sound_start(ma_sound* pSound)
76121 {
76122  if (pSound == NULL) {
76123  return MA_INVALID_ARGS;
76124  }
76125 
76126  /* If the sound is already playing, do nothing. */
76127  if (ma_sound_is_playing(pSound)) {
76128  return MA_SUCCESS;
76129  }
76130 
76131  /* If the sound is at the end it means we want to start from the start again. */
76132  if (ma_sound_at_end(pSound)) {
76133  ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0);
76134  if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) {
76135  return result; /* Failed to seek back to the start. */
76136  }
76137 
76138  /* Make sure we clear the end indicator. */
76139  ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE);
76140  }
76141 
76142  /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */
76143  ma_node_set_state(pSound, ma_node_state_started);
76144 
76145  return MA_SUCCESS;
76146 }
76147 
76148 MA_API ma_result ma_sound_stop(ma_sound* pSound)
76149 {
76150  if (pSound == NULL) {
76151  return MA_INVALID_ARGS;
76152  }
76153 
76154  /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */
76155  ma_node_set_state(pSound, ma_node_state_stopped);
76156 
76157  return MA_SUCCESS;
76158 }
76159 
76160 MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames)
76161 {
76162  if (pSound == NULL) {
76163  return MA_INVALID_ARGS;
76164  }
76165 
76166  /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */
76167  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);
76168 
76169  return MA_SUCCESS;
76170 }
76171 
76172 MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds)
76173 {
76174  ma_uint64 sampleRate;
76175 
76176  if (pSound == NULL) {
76177  return MA_INVALID_ARGS;
76178  }
76179 
76180  sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
76181 
76182  return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000);
76183 }
76184 
76185 MA_API void ma_sound_set_volume(ma_sound* pSound, float volume)
76186 {
76187  if (pSound == NULL) {
76188  return;
76189  }
76190 
76191  ma_engine_node_set_volume(&pSound->engineNode, volume);
76192 }
76193 
76194 MA_API float ma_sound_get_volume(const ma_sound* pSound)
76195 {
76196  float volume = 0;
76197 
76198  if (pSound == NULL) {
76199  return 0;
76200  }
76201 
76202  ma_engine_node_get_volume(&pSound->engineNode, &volume);
76203 
76204  return volume;
76205 }
76206 
76207 MA_API void ma_sound_set_pan(ma_sound* pSound, float pan)
76208 {
76209  if (pSound == NULL) {
76210  return;
76211  }
76212 
76213  ma_panner_set_pan(&pSound->engineNode.panner, pan);
76214 }
76215 
76216 MA_API float ma_sound_get_pan(const ma_sound* pSound)
76217 {
76218  if (pSound == NULL) {
76219  return 0;
76220  }
76221 
76222  return ma_panner_get_pan(&pSound->engineNode.panner);
76223 }
76224 
76225 MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode)
76226 {
76227  if (pSound == NULL) {
76228  return;
76229  }
76230 
76231  ma_panner_set_mode(&pSound->engineNode.panner, panMode);
76232 }
76233 
76234 MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound)
76235 {
76236  if (pSound == NULL) {
76237  return ma_pan_mode_balance;
76238  }
76239 
76240  return ma_panner_get_mode(&pSound->engineNode.panner);
76241 }
76242 
76243 MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch)
76244 {
76245  if (pSound == NULL) {
76246  return;
76247  }
76248 
76249  if (pitch <= 0) {
76250  return;
76251  }
76252 
76253  ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release);
76254 }
76255 
76256 MA_API float ma_sound_get_pitch(const ma_sound* pSound)
76257 {
76258  if (pSound == NULL) {
76259  return 0;
76260  }
76261 
76262  return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */
76263 }
76264 
76265 MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled)
76266 {
76267  if (pSound == NULL) {
76268  return;
76269  }
76270 
76271  ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release);
76272 }
76273 
76274 MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound)
76275 {
76276  if (pSound == NULL) {
76277  return MA_FALSE;
76278  }
76279 
76280  return ma_engine_node_is_spatialization_enabled(&pSound->engineNode);
76281 }
76282 
76283 MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex)
76284 {
76285  if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) {
76286  return;
76287  }
76288 
76289  ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release);
76290 }
76291 
76292 MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound)
76293 {
76294  if (pSound == NULL) {
76295  return MA_LISTENER_INDEX_CLOSEST;
76296  }
76297 
76298  return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire);
76299 }
76300 
76301 MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound)
76302 {
76303  ma_uint32 listenerIndex;
76304 
76305  if (pSound == NULL) {
76306  return 0;
76307  }
76308 
76309  listenerIndex = ma_sound_get_pinned_listener_index(pSound);
76310  if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) {
76311  ma_vec3f position = ma_sound_get_position(pSound);
76312  return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z);
76313  }
76314 
76315  return listenerIndex;
76316 }
76317 
76318 MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound)
76319 {
76320  ma_vec3f relativePos;
76321  ma_engine* pEngine;
76322 
76323  if (pSound == NULL) {
76324  return ma_vec3f_init_3f(0, 0, -1);
76325  }
76326 
76327  pEngine = ma_sound_get_engine(pSound);
76328  if (pEngine == NULL) {
76329  return ma_vec3f_init_3f(0, 0, -1);
76330  }
76331 
76332  ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL);
76333 
76334  return ma_vec3f_normalize(ma_vec3f_neg(relativePos));
76335 }
76336 
76337 MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z)
76338 {
76339  if (pSound == NULL) {
76340  return;
76341  }
76342 
76343  ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z);
76344 }
76345 
76346 MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound)
76347 {
76348  if (pSound == NULL) {
76349  return ma_vec3f_init_3f(0, 0, 0);
76350  }
76351 
76352  return ma_spatializer_get_position(&pSound->engineNode.spatializer);
76353 }
76354 
76355 MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z)
76356 {
76357  if (pSound == NULL) {
76358  return;
76359  }
76360 
76361  ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z);
76362 }
76363 
76364 MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound)
76365 {
76366  if (pSound == NULL) {
76367  return ma_vec3f_init_3f(0, 0, 0);
76368  }
76369 
76370  return ma_spatializer_get_direction(&pSound->engineNode.spatializer);
76371 }
76372 
76373 MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z)
76374 {
76375  if (pSound == NULL) {
76376  return;
76377  }
76378 
76379  ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z);
76380 }
76381 
76382 MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound)
76383 {
76384  if (pSound == NULL) {
76385  return ma_vec3f_init_3f(0, 0, 0);
76386  }
76387 
76388  return ma_spatializer_get_velocity(&pSound->engineNode.spatializer);
76389 }
76390 
76391 MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel)
76392 {
76393  if (pSound == NULL) {
76394  return;
76395  }
76396 
76397  ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel);
76398 }
76399 
76400 MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound)
76401 {
76402  if (pSound == NULL) {
76403  return ma_attenuation_model_none;
76404  }
76405 
76406  return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer);
76407 }
76408 
76409 MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning)
76410 {
76411  if (pSound == NULL) {
76412  return;
76413  }
76414 
76415  ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning);
76416 }
76417 
76418 MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound)
76419 {
76420  if (pSound == NULL) {
76421  return ma_positioning_absolute;
76422  }
76423 
76424  return ma_spatializer_get_positioning(&pSound->engineNode.spatializer);
76425 }
76426 
76427 MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff)
76428 {
76429  if (pSound == NULL) {
76430  return;
76431  }
76432 
76433  ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff);
76434 }
76435 
76436 MA_API float ma_sound_get_rolloff(const ma_sound* pSound)
76437 {
76438  if (pSound == NULL) {
76439  return 0;
76440  }
76441 
76442  return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer);
76443 }
76444 
76445 MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain)
76446 {
76447  if (pSound == NULL) {
76448  return;
76449  }
76450 
76451  ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain);
76452 }
76453 
76454 MA_API float ma_sound_get_min_gain(const ma_sound* pSound)
76455 {
76456  if (pSound == NULL) {
76457  return 0;
76458  }
76459 
76460  return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer);
76461 }
76462 
76463 MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain)
76464 {
76465  if (pSound == NULL) {
76466  return;
76467  }
76468 
76469  ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain);
76470 }
76471 
76472 MA_API float ma_sound_get_max_gain(const ma_sound* pSound)
76473 {
76474  if (pSound == NULL) {
76475  return 0;
76476  }
76477 
76478  return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer);
76479 }
76480 
76481 MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance)
76482 {
76483  if (pSound == NULL) {
76484  return;
76485  }
76486 
76487  ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance);
76488 }
76489 
76490 MA_API float ma_sound_get_min_distance(const ma_sound* pSound)
76491 {
76492  if (pSound == NULL) {
76493  return 0;
76494  }
76495 
76496  return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer);
76497 }
76498 
76499 MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance)
76500 {
76501  if (pSound == NULL) {
76502  return;
76503  }
76504 
76505  ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance);
76506 }
76507 
76508 MA_API float ma_sound_get_max_distance(const ma_sound* pSound)
76509 {
76510  if (pSound == NULL) {
76511  return 0;
76512  }
76513 
76514  return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer);
76515 }
76516 
76517 MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
76518 {
76519  if (pSound == NULL) {
76520  return;
76521  }
76522 
76523  ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain);
76524 }
76525 
76526 MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
76527 {
76528  if (pInnerAngleInRadians != NULL) {
76529  *pInnerAngleInRadians = 0;
76530  }
76531 
76532  if (pOuterAngleInRadians != NULL) {
76533  *pOuterAngleInRadians = 0;
76534  }
76535 
76536  if (pOuterGain != NULL) {
76537  *pOuterGain = 0;
76538  }
76539 
76540  if (pSound == NULL) {
76541  return;
76542  }
76543 
76544  ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
76545 }
76546 
76547 MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor)
76548 {
76549  if (pSound == NULL) {
76550  return;
76551  }
76552 
76553  ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor);
76554 }
76555 
76556 MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound)
76557 {
76558  if (pSound == NULL) {
76559  return 0;
76560  }
76561 
76562  return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer);
76563 }
76564 
76565 MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor)
76566 {
76567  if (pSound == NULL) {
76568  return;
76569  }
76570 
76571  ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor);
76572 }
76573 
76574 MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound)
76575 {
76576  if (pSound == NULL) {
76577  return 1;
76578  }
76579 
76580  return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer);
76581 }
76582 
76583 
76584 MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
76585 {
76586  if (pSound == NULL) {
76587  return;
76588  }
76589 
76590  ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0));
76591 }
76592 
76593 MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
76594 {
76595  if (pSound == NULL) {
76596  return;
76597  }
76598 
76599  ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000);
76600 }
76601 
76602 MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames)
76603 {
76604  if (pSound == NULL) {
76605  return;
76606  }
76607 
76608  /*
76609  We don't want to update the fader at this point because we need to use the engine's current time
76610  to derive the fader's start offset. The timer is being updated on the audio thread so in order to
76611  do this as accurately as possible we'll need to defer this to the audio thread.
76612  */
76613  ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg);
76614  ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd);
76615  ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames);
76616  ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames);
76617 }
76618 
76619 MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds)
76620 {
76621  ma_uint32 sampleRate;
76622 
76623  if (pSound == NULL) {
76624  return;
76625  }
76626 
76627  sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
76628 
76629  ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000);
76630 }
76631 
76632 MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound)
76633 {
76634  if (pSound == NULL) {
76635  return MA_INVALID_ARGS;
76636  }
76637 
76638  return ma_fader_get_current_volume(&pSound->engineNode.fader);
76639 }
76640 
76641 MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
76642 {
76643  if (pSound == NULL) {
76644  return;
76645  }
76646 
76647  ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames);
76648 }
76649 
76650 MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
76651 {
76652  if (pSound == NULL) {
76653  return;
76654  }
76655 
76656  ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
76657 }
76658 
76659 MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
76660 {
76661  if (pSound == NULL) {
76662  return;
76663  }
76664 
76665  ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0);
76666 }
76667 
76668 MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
76669 {
76670  if (pSound == NULL) {
76671  return;
76672  }
76673 
76674  ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
76675 }
76676 
76677 MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames)
76678 {
76679  if (pSound == NULL) {
76680  return;
76681  }
76682 
76683  if (fadeLengthInFrames > 0) {
76684  if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) {
76685  fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames;
76686  }
76687 
76688  ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames);
76689  }
76690 
76691  ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames);
76692 }
76693 
76694 MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds)
76695 {
76696  ma_uint32 sampleRate;
76697 
76698  if (pSound == NULL) {
76699  return;
76700  }
76701 
76702  sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
76703 
76704  ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000);
76705 }
76706 
76707 MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound)
76708 {
76709  if (pSound == NULL) {
76710  return MA_FALSE;
76711  }
76712 
76713  return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started;
76714 }
76715 
76716 MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound)
76717 {
76718  if (pSound == NULL) {
76719  return 0;
76720  }
76721 
76722  return ma_node_get_time(pSound);
76723 }
76724 
76725 MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound)
76726 {
76727  return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
76728 }
76729 
76730 MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping)
76731 {
76732  if (pSound == NULL) {
76733  return;
76734  }
76735 
76736  /* Looping is only a valid concept if the sound is backed by a data source. */
76737  if (pSound->pDataSource == NULL) {
76738  return;
76739  }
76740 
76741  /* The looping state needs to be applied to the data source in order for any looping to actually happen. */
76742  ma_data_source_set_looping(pSound->pDataSource, isLooping);
76743 }
76744 
76745 MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound)
76746 {
76747  if (pSound == NULL) {
76748  return MA_FALSE;
76749  }
76750 
76751  /* There is no notion of looping for sounds that are not backed by a data source. */
76752  if (pSound->pDataSource == NULL) {
76753  return MA_FALSE;
76754  }
76755 
76756  return ma_data_source_is_looping(pSound->pDataSource);
76757 }
76758 
76759 MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound)
76760 {
76761  if (pSound == NULL) {
76762  return MA_FALSE;
76763  }
76764 
76765  /* There is no notion of an end of a sound if it's not backed by a data source. */
76766  if (pSound->pDataSource == NULL) {
76767  return MA_FALSE;
76768  }
76769 
76770  return ma_sound_get_at_end(pSound);
76771 }
76772 
76773 MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex)
76774 {
76775  if (pSound == NULL) {
76776  return MA_INVALID_ARGS;
76777  }
76778 
76779  /* Seeking is only valid for sounds that are backed by a data source. */
76780  if (pSound->pDataSource == NULL) {
76781  return MA_INVALID_OPERATION;
76782  }
76783 
76784  /* 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. */
76785  ma_atomic_exchange_64(&pSound->seekTarget, frameIndex);
76786 
76787  return MA_SUCCESS;
76788 }
76789 
76790 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)
76791 {
76792  if (pSound == NULL) {
76793  return MA_INVALID_ARGS;
76794  }
76795 
76796  /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */
76797  if (pSound->pDataSource == NULL) {
76798  ma_uint32 channels;
76799 
76800  if (pFormat != NULL) {
76801  *pFormat = ma_format_f32;
76802  }
76803 
76804  channels = ma_node_get_input_channels(&pSound->engineNode, 0);
76805  if (pChannels != NULL) {
76806  *pChannels = channels;
76807  }
76808 
76809  if (pSampleRate != NULL) {
76810  *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn;
76811  }
76812 
76813  if (pChannelMap != NULL) {
76814  ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels);
76815  }
76816 
76817  return MA_SUCCESS;
76818  } else {
76819  return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
76820  }
76821 }
76822 
76823 MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor)
76824 {
76825  ma_uint64 seekTarget;
76826 
76827  if (pSound == NULL) {
76828  return MA_INVALID_ARGS;
76829  }
76830 
76831  /* The notion of a cursor is only valid for sounds that are backed by a data source. */
76832  if (pSound->pDataSource == NULL) {
76833  return MA_INVALID_OPERATION;
76834  }
76835 
76836  seekTarget = ma_atomic_load_64(&pSound->seekTarget);
76837  if (seekTarget != MA_SEEK_TARGET_NONE) {
76838  *pCursor = seekTarget;
76839  return MA_SUCCESS;
76840  } else {
76841  return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor);
76842  }
76843 }
76844 
76845 MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength)
76846 {
76847  if (pSound == NULL) {
76848  return MA_INVALID_ARGS;
76849  }
76850 
76851  /* The notion of a sound length is only valid for sounds that are backed by a data source. */
76852  if (pSound->pDataSource == NULL) {
76853  return MA_INVALID_OPERATION;
76854  }
76855 
76856  return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength);
76857 }
76858 
76859 MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor)
76860 {
76861  ma_result result;
76862  ma_uint64 cursorInPCMFrames;
76863  ma_uint32 sampleRate;
76864 
76865  if (pCursor != NULL) {
76866  *pCursor = 0;
76867  }
76868 
76869  result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames);
76870  if (result != MA_SUCCESS) {
76871  return result;
76872  }
76873 
76874  result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0);
76875  if (result != MA_SUCCESS) {
76876  return result;
76877  }
76878 
76879  /* 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. */
76880  *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;
76881 
76882  return MA_SUCCESS;
76883 }
76884 
76885 MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength)
76886 {
76887  if (pSound == NULL) {
76888  return MA_INVALID_ARGS;
76889  }
76890 
76891  /* The notion of a sound length is only valid for sounds that are backed by a data source. */
76892  if (pSound->pDataSource == NULL) {
76893  return MA_INVALID_OPERATION;
76894  }
76895 
76896  return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength);
76897 }
76898 
76899 MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData)
76900 {
76901  if (pSound == NULL) {
76902  return MA_INVALID_ARGS;
76903  }
76904 
76905  /* The notion of an end is only valid for sounds that are backed by a data source. */
76906  if (pSound->pDataSource == NULL) {
76907  return MA_INVALID_OPERATION;
76908  }
76909 
76910  pSound->endCallback = callback;
76911  pSound->pEndCallbackUserData = pUserData;
76912 
76913  return MA_SUCCESS;
76914 }
76915 
76916 
76917 MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup)
76918 {
76919  ma_sound_group_config config = ma_sound_group_config_init_2(pEngine);
76920  config.flags = flags;
76921  config.pInitialAttachment = pParentGroup;
76922  return ma_sound_group_init_ex(pEngine, &config, pGroup);
76923 }
76924 
76925 MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup)
76926 {
76927  ma_sound_config soundConfig;
76928 
76929  if (pGroup == NULL) {
76930  return MA_INVALID_ARGS;
76931  }
76932 
76933  MA_ZERO_OBJECT(pGroup);
76934 
76935  if (pConfig == NULL) {
76936  return MA_INVALID_ARGS;
76937  }
76938 
76939  /* A sound group is just a sound without a data source. */
76940  soundConfig = *pConfig;
76941  soundConfig.pFilePath = NULL;
76942  soundConfig.pFilePathW = NULL;
76943  soundConfig.pDataSource = NULL;
76944 
76945  /*
76946  Groups need to have spatialization disabled by default because I think it'll be pretty rare
76947  that programs will want to spatialize groups (but not unheard of). Certainly it feels like
76948  disabling this by default feels like the right option. Spatialization can be enabled with a
76949  call to ma_sound_group_set_spatialization_enabled().
76950  */
76951  soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION;
76952 
76953  return ma_sound_init_ex(pEngine, &soundConfig, pGroup);
76954 }
76955 
76956 MA_API void ma_sound_group_uninit(ma_sound_group* pGroup)
76957 {
76958  ma_sound_uninit(pGroup);
76959 }
76960 
76961 MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup)
76962 {
76963  return ma_sound_get_engine(pGroup);
76964 }
76965 
76966 MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup)
76967 {
76968  return ma_sound_start(pGroup);
76969 }
76970 
76971 MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup)
76972 {
76973  return ma_sound_stop(pGroup);
76974 }
76975 
76976 MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume)
76977 {
76978  ma_sound_set_volume(pGroup, volume);
76979 }
76980 
76981 MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup)
76982 {
76983  return ma_sound_get_volume(pGroup);
76984 }
76985 
76986 MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan)
76987 {
76988  ma_sound_set_pan(pGroup, pan);
76989 }
76990 
76991 MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup)
76992 {
76993  return ma_sound_get_pan(pGroup);
76994 }
76995 
76996 MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode)
76997 {
76998  ma_sound_set_pan_mode(pGroup, panMode);
76999 }
77000 
77001 MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup)
77002 {
77003  return ma_sound_get_pan_mode(pGroup);
77004 }
77005 
77006 MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch)
77007 {
77008  ma_sound_set_pitch(pGroup, pitch);
77009 }
77010 
77011 MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup)
77012 {
77013  return ma_sound_get_pitch(pGroup);
77014 }
77015 
77016 MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled)
77017 {
77018  ma_sound_set_spatialization_enabled(pGroup, enabled);
77019 }
77020 
77021 MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup)
77022 {
77023  return ma_sound_is_spatialization_enabled(pGroup);
77024 }
77025 
77026 MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex)
77027 {
77028  ma_sound_set_pinned_listener_index(pGroup, listenerIndex);
77029 }
77030 
77031 MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup)
77032 {
77033  return ma_sound_get_pinned_listener_index(pGroup);
77034 }
77035 
77036 MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup)
77037 {
77038  return ma_sound_get_listener_index(pGroup);
77039 }
77040 
77041 MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup)
77042 {
77043  return ma_sound_get_direction_to_listener(pGroup);
77044 }
77045 
77046 MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z)
77047 {
77048  ma_sound_set_position(pGroup, x, y, z);
77049 }
77050 
77051 MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup)
77052 {
77053  return ma_sound_get_position(pGroup);
77054 }
77055 
77056 MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z)
77057 {
77058  ma_sound_set_direction(pGroup, x, y, z);
77059 }
77060 
77061 MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup)
77062 {
77063  return ma_sound_get_direction(pGroup);
77064 }
77065 
77066 MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z)
77067 {
77068  ma_sound_set_velocity(pGroup, x, y, z);
77069 }
77070 
77071 MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup)
77072 {
77073  return ma_sound_get_velocity(pGroup);
77074 }
77075 
77076 MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel)
77077 {
77078  ma_sound_set_attenuation_model(pGroup, attenuationModel);
77079 }
77080 
77081 MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup)
77082 {
77083  return ma_sound_get_attenuation_model(pGroup);
77084 }
77085 
77086 MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning)
77087 {
77088  ma_sound_set_positioning(pGroup, positioning);
77089 }
77090 
77091 MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup)
77092 {
77093  return ma_sound_get_positioning(pGroup);
77094 }
77095 
77096 MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff)
77097 {
77098  ma_sound_set_rolloff(pGroup, rolloff);
77099 }
77100 
77101 MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup)
77102 {
77103  return ma_sound_get_rolloff(pGroup);
77104 }
77105 
77106 MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain)
77107 {
77108  ma_sound_set_min_gain(pGroup, minGain);
77109 }
77110 
77111 MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup)
77112 {
77113  return ma_sound_get_min_gain(pGroup);
77114 }
77115 
77116 MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain)
77117 {
77118  ma_sound_set_max_gain(pGroup, maxGain);
77119 }
77120 
77121 MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup)
77122 {
77123  return ma_sound_get_max_gain(pGroup);
77124 }
77125 
77126 MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance)
77127 {
77128  ma_sound_set_min_distance(pGroup, minDistance);
77129 }
77130 
77131 MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup)
77132 {
77133  return ma_sound_get_min_distance(pGroup);
77134 }
77135 
77136 MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance)
77137 {
77138  ma_sound_set_max_distance(pGroup, maxDistance);
77139 }
77140 
77141 MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup)
77142 {
77143  return ma_sound_get_max_distance(pGroup);
77144 }
77145 
77146 MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
77147 {
77148  ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain);
77149 }
77150 
77151 MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
77152 {
77153  ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
77154 }
77155 
77156 MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor)
77157 {
77158  ma_sound_set_doppler_factor(pGroup, dopplerFactor);
77159 }
77160 
77161 MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup)
77162 {
77163  return ma_sound_get_doppler_factor(pGroup);
77164 }
77165 
77166 MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor)
77167 {
77168  ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor);
77169 }
77170 
77171 MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup)
77172 {
77173  return ma_sound_get_directional_attenuation_factor(pGroup);
77174 }
77175 
77176 MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
77177 {
77178  ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames);
77179 }
77180 
77181 MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
77182 {
77183  ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds);
77184 }
77185 
77186 MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup)
77187 {
77188  return ma_sound_get_current_fade_volume(pGroup);
77189 }
77190 
77191 MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
77192 {
77193  ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
77194 }
77195 
77196 MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
77197 {
77198  ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
77199 }
77200 
77201 MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
77202 {
77203  ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
77204 }
77205 
77206 MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
77207 {
77208  ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
77209 }
77210 
77211 MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup)
77212 {
77213  return ma_sound_is_playing(pGroup);
77214 }
77215 
77216 MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup)
77217 {
77218  return ma_sound_get_time_in_pcm_frames(pGroup);
77219 }
77220 #endif /* MA_NO_ENGINE */
77221 /* END SECTION: miniaudio_engine.c */
77222 
77223 
77224 
77225 /**************************************************************************************************************************************************************
77226 ***************************************************************************************************************************************************************
77227 
77228 Auto Generated
77229 ==============
77230 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
77231 code below please report the bug to the respective repository for the relevant project (probably dr_libs).
77232 
77233 ***************************************************************************************************************************************************************
77234 **************************************************************************************************************************************************************/
77235 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
77236 #if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
77237 /* dr_wav_c begin */
77238 #ifndef ma_dr_wav_c
77239 #define ma_dr_wav_c
77240 #ifdef __MRC__
77241 #pragma options opt off
77242 #endif
77243 #include <stdlib.h>
77244 #include <string.h>
77245 #include <limits.h>
77246 #ifndef MA_DR_WAV_NO_STDIO
77247 #include <stdio.h>
77248 #ifndef MA_DR_WAV_NO_WCHAR
77249 #include <wchar.h>
77250 #endif
77251 #endif
77252 #ifndef MA_DR_WAV_ASSERT
77253 #include <assert.h>
77254 #define MA_DR_WAV_ASSERT(expression) assert(expression)
77255 #endif
77256 #ifndef MA_DR_WAV_MALLOC
77257 #define MA_DR_WAV_MALLOC(sz) malloc((sz))
77258 #endif
77259 #ifndef MA_DR_WAV_REALLOC
77260 #define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz))
77261 #endif
77262 #ifndef MA_DR_WAV_FREE
77263 #define MA_DR_WAV_FREE(p) free((p))
77264 #endif
77265 #ifndef MA_DR_WAV_COPY_MEMORY
77266 #define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
77267 #endif
77268 #ifndef MA_DR_WAV_ZERO_MEMORY
77269 #define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
77270 #endif
77271 #ifndef MA_DR_WAV_ZERO_OBJECT
77272 #define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p))
77273 #endif
77274 #define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0]))
77275 #define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
77276 #define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b))
77277 #define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b))
77278 #define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x))))
77279 #define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
77280 #define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32
77281 #define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32))
77282 #define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF))
77283 #if defined(_MSC_VER) && _MSC_VER >= 1400
77284  #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
77285  #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
77286  #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
77287 #elif defined(__clang__)
77288  #if defined(__has_builtin)
77289  #if __has_builtin(__builtin_bswap16)
77290  #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
77291  #endif
77292  #if __has_builtin(__builtin_bswap32)
77293  #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
77294  #endif
77295  #if __has_builtin(__builtin_bswap64)
77296  #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
77297  #endif
77298  #endif
77299 #elif defined(__GNUC__)
77300  #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
77301  #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
77302  #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
77303  #endif
77304  #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
77305  #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
77306  #endif
77307 #endif
77308 MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
77309 {
77310  if (pMajor) {
77311  *pMajor = MA_DR_WAV_VERSION_MAJOR;
77312  }
77313  if (pMinor) {
77314  *pMinor = MA_DR_WAV_VERSION_MINOR;
77315  }
77316  if (pRevision) {
77317  *pRevision = MA_DR_WAV_VERSION_REVISION;
77318  }
77319 }
77320 MA_API const char* ma_dr_wav_version_string(void)
77321 {
77322  return MA_DR_WAV_VERSION_STRING;
77323 }
77324 #ifndef MA_DR_WAV_MAX_SAMPLE_RATE
77325 #define MA_DR_WAV_MAX_SAMPLE_RATE 384000
77326 #endif
77327 #ifndef MA_DR_WAV_MAX_CHANNELS
77328 #define MA_DR_WAV_MAX_CHANNELS 256
77329 #endif
77330 #ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE
77331 #define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64
77332 #endif
77333 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};
77334 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};
77335 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};
77336 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};
77337 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};
77338 static MA_INLINE int ma_dr_wav__is_little_endian(void)
77339 {
77340 #if defined(MA_X86) || defined(MA_X64)
77341  return MA_TRUE;
77342 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
77343  return MA_TRUE;
77344 #else
77345  int n = 1;
77346  return (*(char*)&n) == 1;
77347 #endif
77348 }
77349 static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid)
77350 {
77351  int i;
77352  for (i = 0; i < 16; ++i) {
77353  guid[i] = data[i];
77354  }
77355 }
77356 static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n)
77357 {
77358 #ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
77359  #if defined(_MSC_VER)
77360  return _byteswap_ushort(n);
77361  #elif defined(__GNUC__) || defined(__clang__)
77362  return __builtin_bswap16(n);
77363  #else
77364  #error "This compiler does not support the byte swap intrinsic."
77365  #endif
77366 #else
77367  return ((n & 0xFF00) >> 8) |
77368  ((n & 0x00FF) << 8);
77369 #endif
77370 }
77371 static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n)
77372 {
77373 #ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
77374  #if defined(_MSC_VER)
77375  return _byteswap_ulong(n);
77376  #elif defined(__GNUC__) || defined(__clang__)
77377  #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT)
77378  ma_uint32 r;
77379  __asm__ __volatile__ (
77380  #if defined(MA_64BIT)
77381  "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
77382  #else
77383  "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
77384  #endif
77385  );
77386  return r;
77387  #else
77388  return __builtin_bswap32(n);
77389  #endif
77390  #else
77391  #error "This compiler does not support the byte swap intrinsic."
77392  #endif
77393 #else
77394  return ((n & 0xFF000000) >> 24) |
77395  ((n & 0x00FF0000) >> 8) |
77396  ((n & 0x0000FF00) << 8) |
77397  ((n & 0x000000FF) << 24);
77398 #endif
77399 }
77400 static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n)
77401 {
77402 #ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
77403  #if defined(_MSC_VER)
77404  return _byteswap_uint64(n);
77405  #elif defined(__GNUC__) || defined(__clang__)
77406  return __builtin_bswap64(n);
77407  #else
77408  #error "This compiler does not support the byte swap intrinsic."
77409  #endif
77410 #else
77411  return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) |
77412  ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) |
77413  ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) |
77414  ((n & ((ma_uint64)0x000000FF << 32)) >> 8) |
77415  ((n & ((ma_uint64)0xFF000000 )) << 8) |
77416  ((n & ((ma_uint64)0x00FF0000 )) << 24) |
77417  ((n & ((ma_uint64)0x0000FF00 )) << 40) |
77418  ((n & ((ma_uint64)0x000000FF )) << 56);
77419 #endif
77420 }
77421 static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n)
77422 {
77423  return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n);
77424 }
77425 static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount)
77426 {
77427  ma_uint64 iSample;
77428  for (iSample = 0; iSample < sampleCount; iSample += 1) {
77429  pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]);
77430  }
77431 }
77432 static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p)
77433 {
77434  ma_uint8 t;
77435  t = p[0];
77436  p[0] = p[2];
77437  p[2] = t;
77438 }
77439 static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount)
77440 {
77441  ma_uint64 iSample;
77442  for (iSample = 0; iSample < sampleCount; iSample += 1) {
77443  ma_uint8* pSample = pSamples + (iSample*3);
77444  ma_dr_wav__bswap_s24(pSample);
77445  }
77446 }
77447 static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n)
77448 {
77449  return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n);
77450 }
77451 static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount)
77452 {
77453  ma_uint64 iSample;
77454  for (iSample = 0; iSample < sampleCount; iSample += 1) {
77455  pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]);
77456  }
77457 }
77458 static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n)
77459 {
77460  return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n);
77461 }
77462 static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount)
77463 {
77464  ma_uint64 iSample;
77465  for (iSample = 0; iSample < sampleCount; iSample += 1) {
77466  pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]);
77467  }
77468 }
77469 static MA_INLINE float ma_dr_wav__bswap_f32(float n)
77470 {
77471  union {
77472  ma_uint32 i;
77473  float f;
77474  } x;
77475  x.f = n;
77476  x.i = ma_dr_wav__bswap32(x.i);
77477  return x.f;
77478 }
77479 static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount)
77480 {
77481  ma_uint64 iSample;
77482  for (iSample = 0; iSample < sampleCount; iSample += 1) {
77483  pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]);
77484  }
77485 }
77486 static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample)
77487 {
77488  switch (bytesPerSample)
77489  {
77490  case 1:
77491  {
77492  } break;
77493  case 2:
77494  {
77495  ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount);
77496  } break;
77497  case 3:
77498  {
77499  ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount);
77500  } break;
77501  case 4:
77502  {
77503  ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount);
77504  } break;
77505  case 8:
77506  {
77507  ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount);
77508  } break;
77509  default:
77510  {
77511  MA_DR_WAV_ASSERT(MA_FALSE);
77512  } break;
77513  }
77514 }
77515 MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container)
77516 {
77517  if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) {
77518  return MA_TRUE;
77519  } else {
77520  return MA_FALSE;
77521  }
77522 }
77523 MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data)
77524 {
77525  return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8);
77526 }
77527 MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data)
77528 {
77529  return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8);
77530 }
77531 MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container)
77532 {
77533  if (ma_dr_wav_is_container_be(container)) {
77534  return ma_dr_wav_bytes_to_u16_be(data);
77535  } else {
77536  return ma_dr_wav_bytes_to_u16_le(data);
77537  }
77538 }
77539 MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data)
77540 {
77541  return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24);
77542 }
77543 MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data)
77544 {
77545  return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24);
77546 }
77547 MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container)
77548 {
77549  if (ma_dr_wav_is_container_be(container)) {
77550  return ma_dr_wav_bytes_to_u32_be(data);
77551  } else {
77552  return ma_dr_wav_bytes_to_u32_le(data);
77553  }
77554 }
77555 MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data)
77556 {
77557  ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1];
77558  ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0);
77559  ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0);
77560  ma_uint64 significand = (hi << 32) | lo;
77561  int sign = exponent >> 15;
77562  exponent &= 0x7FFF;
77563  if (exponent == 0 && significand == 0) {
77564  return 0;
77565  } else if (exponent == 0x7FFF) {
77566  return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX;
77567  }
77568  exponent -= 16383;
77569  if (exponent > 63) {
77570  return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX;
77571  } else if (exponent < 1) {
77572  return 0;
77573  }
77574  significand >>= (63 - exponent);
77575  if (sign) {
77576  return -(ma_int64)significand;
77577  } else {
77578  return (ma_int64)significand;
77579  }
77580 }
77581 MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData)
77582 {
77583  (void)pUserData;
77584  return MA_DR_WAV_MALLOC(sz);
77585 }
77586 MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData)
77587 {
77588  (void)pUserData;
77589  return MA_DR_WAV_REALLOC(p, sz);
77590 }
77591 MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData)
77592 {
77593  (void)pUserData;
77594  MA_DR_WAV_FREE(p);
77595 }
77596 MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
77597 {
77598  if (pAllocationCallbacks == NULL) {
77599  return NULL;
77600  }
77601  if (pAllocationCallbacks->onMalloc != NULL) {
77602  return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
77603  }
77604  if (pAllocationCallbacks->onRealloc != NULL) {
77605  return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
77606  }
77607  return NULL;
77608 }
77609 MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
77610 {
77611  if (pAllocationCallbacks == NULL) {
77612  return NULL;
77613  }
77614  if (pAllocationCallbacks->onRealloc != NULL) {
77615  return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
77616  }
77617  if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
77618  void* p2;
77619  p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
77620  if (p2 == NULL) {
77621  return NULL;
77622  }
77623  if (p != NULL) {
77624  MA_DR_WAV_COPY_MEMORY(p2, p, szOld);
77625  pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
77626  }
77627  return p2;
77628  }
77629  return NULL;
77630 }
77631 MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
77632 {
77633  if (p == NULL || pAllocationCallbacks == NULL) {
77634  return;
77635  }
77636  if (pAllocationCallbacks->onFree != NULL) {
77637  pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
77638  }
77639 }
77640 MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks)
77641 {
77642  if (pAllocationCallbacks != NULL) {
77643  return *pAllocationCallbacks;
77644  } else {
77645  ma_allocation_callbacks allocationCallbacks;
77646  allocationCallbacks.pUserData = NULL;
77647  allocationCallbacks.onMalloc = ma_dr_wav__malloc_default;
77648  allocationCallbacks.onRealloc = ma_dr_wav__realloc_default;
77649  allocationCallbacks.onFree = ma_dr_wav__free_default;
77650  return allocationCallbacks;
77651  }
77652 }
77653 static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag)
77654 {
77655  return
77656  formatTag == MA_DR_WAVE_FORMAT_ADPCM ||
77657  formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM;
77658 }
77659 MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize)
77660 {
77661  return (unsigned int)(chunkSize % 2);
77662 }
77663 MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize)
77664 {
77665  return (unsigned int)(chunkSize % 8);
77666 }
77667 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut);
77668 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut);
77669 MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount);
77670 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)
77671 {
77672  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) {
77673  ma_uint8 sizeInBytes[4];
77674  if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
77675  return MA_AT_END;
77676  }
77677  if (onRead(pUserData, sizeInBytes, 4) != 4) {
77678  return MA_INVALID_FILE;
77679  }
77680  pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container);
77681  pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
77682  *pRunningBytesReadOut += 8;
77683  } else if (container == ma_dr_wav_container_w64) {
77684  ma_uint8 sizeInBytes[8];
77685  if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
77686  return MA_AT_END;
77687  }
77688  if (onRead(pUserData, sizeInBytes, 8) != 8) {
77689  return MA_INVALID_FILE;
77690  }
77691  pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24;
77692  pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
77693  *pRunningBytesReadOut += 24;
77694  } else {
77695  return MA_INVALID_FILE;
77696  }
77697  return MA_SUCCESS;
77698 }
77699 MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData)
77700 {
77701  ma_uint64 bytesRemainingToSeek = offset;
77702  while (bytesRemainingToSeek > 0) {
77703  if (bytesRemainingToSeek > 0x7FFFFFFF) {
77704  if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) {
77705  return MA_FALSE;
77706  }
77707  bytesRemainingToSeek -= 0x7FFFFFFF;
77708  } else {
77709  if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) {
77710  return MA_FALSE;
77711  }
77712  bytesRemainingToSeek = 0;
77713  }
77714  }
77715  return MA_TRUE;
77716 }
77717 MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData)
77718 {
77719  if (offset <= 0x7FFFFFFF) {
77720  return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start);
77721  }
77722  if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) {
77723  return MA_FALSE;
77724  }
77725  offset -= 0x7FFFFFFF;
77726  for (;;) {
77727  if (offset <= 0x7FFFFFFF) {
77728  return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current);
77729  }
77730  if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) {
77731  return MA_FALSE;
77732  }
77733  offset -= 0x7FFFFFFF;
77734  }
77735 }
77736 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)
77737 {
77738  size_t bytesRead;
77739  MA_DR_WAV_ASSERT(onRead != NULL);
77740  MA_DR_WAV_ASSERT(pCursor != NULL);
77741  bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
77742  *pCursor += bytesRead;
77743  return bytesRead;
77744 }
77745 #if 0
77746 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)
77747 {
77748  MA_DR_WAV_ASSERT(onSeek != NULL);
77749  MA_DR_WAV_ASSERT(pCursor != NULL);
77750  if (!onSeek(pUserData, offset, origin)) {
77751  return MA_FALSE;
77752  }
77753  if (origin == ma_dr_wav_seek_origin_start) {
77754  *pCursor = offset;
77755  } else {
77756  *pCursor += offset;
77757  }
77758  return MA_TRUE;
77759 }
77760 #endif
77761 #define MA_DR_WAV_SMPL_BYTES 36
77762 #define MA_DR_WAV_SMPL_LOOP_BYTES 24
77763 #define MA_DR_WAV_INST_BYTES 7
77764 #define MA_DR_WAV_ACID_BYTES 24
77765 #define MA_DR_WAV_CUE_BYTES 4
77766 #define MA_DR_WAV_BEXT_BYTES 602
77767 #define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256
77768 #define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32
77769 #define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32
77770 #define MA_DR_WAV_BEXT_RESERVED_BYTES 180
77771 #define MA_DR_WAV_BEXT_UMID_BYTES 64
77772 #define MA_DR_WAV_CUE_POINT_BYTES 24
77773 #define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4
77774 #define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20
77775 #define MA_DR_WAV_METADATA_ALIGNMENT 8
77776 typedef enum
77777 {
77778  ma_dr_wav__metadata_parser_stage_count,
77779  ma_dr_wav__metadata_parser_stage_read
77780 } ma_dr_wav__metadata_parser_stage;
77781 typedef struct
77782 {
77783  ma_dr_wav_read_proc onRead;
77784  ma_dr_wav_seek_proc onSeek;
77785  void *pReadSeekUserData;
77786  ma_dr_wav__metadata_parser_stage stage;
77787  ma_dr_wav_metadata *pMetadata;
77788  ma_uint32 metadataCount;
77789  ma_uint8 *pData;
77790  ma_uint8 *pDataCursor;
77791  ma_uint64 metadataCursor;
77792  ma_uint64 extraCapacity;
77793 } ma_dr_wav__metadata_parser;
77794 MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser)
77795 {
77796  ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity;
77797  if (cap > MA_SIZE_MAX) {
77798  return 0;
77799  }
77800  return (size_t)cap;
77801 }
77802 MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align)
77803 {
77804  ma_uint8* pResult;
77805  if (align) {
77806  ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align;
77807  if (modulo != 0) {
77808  pParser->pDataCursor += align - modulo;
77809  }
77810  }
77811  pResult = pParser->pDataCursor;
77812  MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser)));
77813  pParser->pDataCursor += size;
77814  return pResult;
77815 }
77816 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)
77817 {
77818  size_t extra = bytes + (align ? (align - 1) : 0);
77819  pParser->extraCapacity += extra;
77820 }
77821 MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks)
77822 {
77823  if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
77824  pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);
77825  pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
77826  pParser->pDataCursor = pParser->pData;
77827  if (pParser->pData == NULL) {
77828  return MA_OUT_OF_MEMORY;
77829  }
77830  pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1);
77831  pParser->metadataCursor = 0;
77832  }
77833  return MA_SUCCESS;
77834 }
77835 MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor)
77836 {
77837  if (pCursor != NULL) {
77838  return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);
77839  } else {
77840  return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);
77841  }
77842 }
77843 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)
77844 {
77845  ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES];
77846  ma_uint64 totalBytesRead = 0;
77847  size_t bytesJustRead;
77848  if (pMetadata == NULL) {
77849  return 0;
77850  }
77851  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);
77852  MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
77853  MA_DR_WAV_ASSERT(pChunkHeader != NULL);
77854  if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
77855  ma_uint32 iSampleLoop;
77856  pMetadata->type = ma_dr_wav_metadata_type_smpl;
77857  pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0);
77858  pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4);
77859  pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8);
77860  pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12);
77861  pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16);
77862  pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20);
77863  pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24);
77864  pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28);
77865  pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32);
77866  if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) {
77867  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);
77868  for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
77869  ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES];
77870  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
77871  if (bytesJustRead == sizeof(smplLoopData)) {
77872  pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0);
77873  pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4);
77874  pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8);
77875  pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12);
77876  pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16);
77877  pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20);
77878  } else {
77879  break;
77880  }
77881  }
77882  if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
77883  pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);
77884  MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);
77885  ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
77886  }
77887  }
77888  }
77889  return totalBytesRead;
77890 }
77891 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)
77892 {
77893  ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES];
77894  ma_uint64 totalBytesRead = 0;
77895  size_t bytesJustRead;
77896  if (pMetadata == NULL) {
77897  return 0;
77898  }
77899  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);
77900  MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
77901  if (bytesJustRead == sizeof(cueHeaderSectionData)) {
77902  pMetadata->type = ma_dr_wav_metadata_type_cue;
77903  pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData);
77904  if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) {
77905  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);
77906  MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);
77907  if (pMetadata->data.cue.cuePointCount > 0) {
77908  ma_uint32 iCuePoint;
77909  for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
77910  ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES];
77911  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);
77912  if (bytesJustRead == sizeof(cuePointData)) {
77913  pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0);
77914  pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4);
77915  pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8];
77916  pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9];
77917  pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10];
77918  pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
77919  pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12);
77920  pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16);
77921  pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20);
77922  } else {
77923  break;
77924  }
77925  }
77926  }
77927  }
77928  }
77929  return totalBytesRead;
77930 }
77931 MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata)
77932 {
77933  ma_uint8 instData[MA_DR_WAV_INST_BYTES];
77934  ma_uint64 bytesRead;
77935  if (pMetadata == NULL) {
77936  return 0;
77937  }
77938  bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);
77939  MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
77940  if (bytesRead == sizeof(instData)) {
77941  pMetadata->type = ma_dr_wav_metadata_type_inst;
77942  pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0];
77943  pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1];
77944  pMetadata->data.inst.gainDecibels = (ma_int8)instData[2];
77945  pMetadata->data.inst.lowNote = (ma_int8)instData[3];
77946  pMetadata->data.inst.highNote = (ma_int8)instData[4];
77947  pMetadata->data.inst.lowVelocity = (ma_int8)instData[5];
77948  pMetadata->data.inst.highVelocity = (ma_int8)instData[6];
77949  }
77950  return bytesRead;
77951 }
77952 MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata)
77953 {
77954  ma_uint8 acidData[MA_DR_WAV_ACID_BYTES];
77955  ma_uint64 bytesRead;
77956  if (pMetadata == NULL) {
77957  return 0;
77958  }
77959  bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);
77960  MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
77961  if (bytesRead == sizeof(acidData)) {
77962  pMetadata->type = ma_dr_wav_metadata_type_acid;
77963  pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0);
77964  pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4);
77965  pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6);
77966  pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8);
77967  pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12);
77968  pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16);
77969  pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18);
77970  pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20);
77971  }
77972  return bytesRead;
77973 }
77974 MA_PRIVATE size_t ma_dr_wav__strlen(const char* str)
77975 {
77976  size_t result = 0;
77977  while (*str++) {
77978  result += 1;
77979  }
77980  return result;
77981 }
77982 MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead)
77983 {
77984  size_t result = 0;
77985  while (*str++ && result < maxToRead) {
77986  result += 1;
77987  }
77988  return result;
77989 }
77990 MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead)
77991 {
77992  size_t len = ma_dr_wav__strlen_clamped(str, maxToRead);
77993  if (len) {
77994  char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1);
77995  MA_DR_WAV_ASSERT(result != NULL);
77996  MA_DR_WAV_COPY_MEMORY(result, str, len);
77997  result[len] = '\0';
77998  return result;
77999  } else {
78000  return NULL;
78001  }
78002 }
78003 typedef struct
78004 {
78005  const void* pBuffer;
78006  size_t sizeInBytes;
78007  size_t cursor;
78008 } ma_dr_wav_buffer_reader;
78009 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader)
78010 {
78011  MA_DR_WAV_ASSERT(pBuffer != NULL);
78012  MA_DR_WAV_ASSERT(pReader != NULL);
78013  MA_DR_WAV_ZERO_OBJECT(pReader);
78014  pReader->pBuffer = pBuffer;
78015  pReader->sizeInBytes = sizeInBytes;
78016  pReader->cursor = 0;
78017  return MA_SUCCESS;
78018 }
78019 MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader)
78020 {
78021  MA_DR_WAV_ASSERT(pReader != NULL);
78022  return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor);
78023 }
78024 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek)
78025 {
78026  MA_DR_WAV_ASSERT(pReader != NULL);
78027  if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) {
78028  return MA_BAD_SEEK;
78029  }
78030  pReader->cursor += bytesToSeek;
78031  return MA_SUCCESS;
78032 }
78033 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead)
78034 {
78035  ma_result result = MA_SUCCESS;
78036  size_t bytesRemaining;
78037  MA_DR_WAV_ASSERT(pReader != NULL);
78038  if (pBytesRead != NULL) {
78039  *pBytesRead = 0;
78040  }
78041  bytesRemaining = (pReader->sizeInBytes - pReader->cursor);
78042  if (bytesToRead > bytesRemaining) {
78043  bytesToRead = bytesRemaining;
78044  }
78045  if (pDst == NULL) {
78046  result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead);
78047  } else {
78048  MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead);
78049  pReader->cursor += bytesToRead;
78050  }
78051  MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes);
78052  if (result == MA_SUCCESS) {
78053  if (pBytesRead != NULL) {
78054  *pBytesRead = bytesToRead;
78055  }
78056  }
78057  return MA_SUCCESS;
78058 }
78059 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst)
78060 {
78061  ma_result result;
78062  size_t bytesRead;
78063  ma_uint8 data[2];
78064  MA_DR_WAV_ASSERT(pReader != NULL);
78065  MA_DR_WAV_ASSERT(pDst != NULL);
78066  *pDst = 0;
78067  result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
78068  if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) {
78069  return result;
78070  }
78071  *pDst = ma_dr_wav_bytes_to_u16(data);
78072  return MA_SUCCESS;
78073 }
78074 MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst)
78075 {
78076  ma_result result;
78077  size_t bytesRead;
78078  ma_uint8 data[4];
78079  MA_DR_WAV_ASSERT(pReader != NULL);
78080  MA_DR_WAV_ASSERT(pDst != NULL);
78081  *pDst = 0;
78082  result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
78083  if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) {
78084  return result;
78085  }
78086  *pDst = ma_dr_wav_bytes_to_u32(data);
78087  return MA_SUCCESS;
78088 }
78089 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)
78090 {
78091  ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES];
78092  size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
78093  MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
78094  if (bytesRead == sizeof(bextData)) {
78095  ma_dr_wav_buffer_reader reader;
78096  ma_uint32 timeReferenceLow;
78097  ma_uint32 timeReferenceHigh;
78098  size_t extraBytes;
78099  pMetadata->type = ma_dr_wav_metadata_type_bext;
78100  if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) {
78101  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);
78102  ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES);
78103  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);
78104  ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);
78105  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);
78106  ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);
78107  ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL);
78108  ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL);
78109  ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow);
78110  ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh);
78111  pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow;
78112  ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version);
78113  pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1);
78114  ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL);
78115  ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue);
78116  ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange);
78117  ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel);
78118  ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness);
78119  ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness);
78120  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));
78121  extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES);
78122  if (extraBytes > 0) {
78123  pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1);
78124  MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
78125  bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
78126  pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory);
78127  } else {
78128  pMetadata->data.bext.pCodingHistory = NULL;
78129  pMetadata->data.bext.codingHistorySize = 0;
78130  }
78131  }
78132  }
78133  return bytesRead;
78134 }
78135 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)
78136 {
78137  ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES];
78138  ma_uint64 totalBytesRead = 0;
78139  size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
78140  MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
78141  if (bytesJustRead == sizeof(cueIDBuffer)) {
78142  ma_uint32 sizeIncludingNullTerminator;
78143  pMetadata->type = type;
78144  pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer);
78145  sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
78146  if (sizeIncludingNullTerminator > 0) {
78147  pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;
78148  pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
78149  MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
78150  ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);
78151  } else {
78152  pMetadata->data.labelOrNote.stringLength = 0;
78153  pMetadata->data.labelOrNote.pString = NULL;
78154  }
78155  }
78156  return totalBytesRead;
78157 }
78158 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)
78159 {
78160  ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES];
78161  ma_uint64 totalBytesRead = 0;
78162  size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);
78163  MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
78164  if (bytesJustRead == sizeof(buffer)) {
78165  ma_uint32 sizeIncludingNullTerminator;
78166  pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region;
78167  pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0);
78168  pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4);
78169  pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];
78170  pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];
78171  pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];
78172  pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];
78173  pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12);
78174  pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14);
78175  pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16);
78176  pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18);
78177  sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
78178  if (sizeIncludingNullTerminator > 0) {
78179  pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;
78180  pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
78181  MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
78182  ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);
78183  } else {
78184  pMetadata->data.labelledCueRegion.stringLength = 0;
78185  pMetadata->data.labelledCueRegion.pString = NULL;
78186  }
78187  }
78188  return totalBytesRead;
78189 }
78190 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)
78191 {
78192  ma_uint64 bytesRead = 0;
78193  ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize;
78194  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78195  pParser->metadataCount += 1;
78196  ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);
78197  } else {
78198  ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
78199  pMetadata->type = type;
78200  if (stringSizeWithNullTerminator > 0) {
78201  pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;
78202  pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);
78203  MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL);
78204  bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);
78205  if (bytesRead == chunkSize) {
78206  pParser->metadataCursor += 1;
78207  } else {
78208  }
78209  } else {
78210  pMetadata->data.infoText.stringLength = 0;
78211  pMetadata->data.infoText.pString = NULL;
78212  pParser->metadataCursor += 1;
78213  }
78214  }
78215  return bytesRead;
78216 }
78217 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)
78218 {
78219  ma_uint64 bytesRead = 0;
78220  if (location == ma_dr_wav_metadata_location_invalid) {
78221  return 0;
78222  }
78223  if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) {
78224  return 0;
78225  }
78226  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78227  pParser->metadataCount += 1;
78228  ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);
78229  } else {
78230  ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
78231  pMetadata->type = ma_dr_wav_metadata_type_unknown;
78232  pMetadata->data.unknown.chunkLocation = location;
78233  pMetadata->data.unknown.id[0] = pChunkId[0];
78234  pMetadata->data.unknown.id[1] = pChunkId[1];
78235  pMetadata->data.unknown.id[2] = pChunkId[2];
78236  pMetadata->data.unknown.id[3] = pChunkId[3];
78237  pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize;
78238  pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1);
78239  MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL);
78240  bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);
78241  if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {
78242  pParser->metadataCursor += 1;
78243  } else {
78244  }
78245  }
78246  return bytesRead;
78247 }
78248 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)
78249 {
78250  return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID);
78251 }
78252 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)
78253 {
78254  const ma_uint8 *pChunkID = pChunkHeader->id.fourcc;
78255  ma_uint64 bytesRead = 0;
78256  if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) {
78257  if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) {
78258  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78259  ma_uint8 buffer[4];
78260  size_t bytesJustRead;
78261  if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) {
78262  return bytesRead;
78263  }
78264  bytesRead += 28;
78265  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
78266  if (bytesJustRead == sizeof(buffer)) {
78267  ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer);
78268  ma_uint64 calculatedLoopCount;
78269  calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES;
78270  if (calculatedLoopCount == loopCount) {
78271  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
78272  if (bytesJustRead == sizeof(buffer)) {
78273  ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer);
78274  pParser->metadataCount += 1;
78275  ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT);
78276  ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);
78277  }
78278  } else {
78279  }
78280  }
78281  } else {
78282  bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
78283  if (bytesRead == pChunkHeader->sizeInBytes) {
78284  pParser->metadataCursor += 1;
78285  } else {
78286  }
78287  }
78288  } else {
78289  }
78290  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) {
78291  if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) {
78292  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78293  pParser->metadataCount += 1;
78294  } else {
78295  bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
78296  if (bytesRead == pChunkHeader->sizeInBytes) {
78297  pParser->metadataCursor += 1;
78298  } else {
78299  }
78300  }
78301  } else {
78302  }
78303  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) {
78304  if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) {
78305  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78306  pParser->metadataCount += 1;
78307  } else {
78308  bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
78309  if (bytesRead == pChunkHeader->sizeInBytes) {
78310  pParser->metadataCursor += 1;
78311  } else {
78312  }
78313  }
78314  } else {
78315  }
78316  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) {
78317  if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) {
78318  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78319  size_t cueCount;
78320  pParser->metadataCount += 1;
78321  cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES;
78322  ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT);
78323  } else {
78324  bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
78325  if (bytesRead == pChunkHeader->sizeInBytes) {
78326  pParser->metadataCursor += 1;
78327  } else {
78328  }
78329  }
78330  } else {
78331  }
78332  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) {
78333  if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) {
78334  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78335  char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1];
78336  size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES;
78337  size_t bytesJustRead;
78338  buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0';
78339  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead);
78340  if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) {
78341  return bytesRead;
78342  }
78343  allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
78344  buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
78345  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
78346  if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) {
78347  return bytesRead;
78348  }
78349  allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
78350  buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
78351  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
78352  if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) {
78353  return bytesRead;
78354  }
78355  allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
78356  allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES;
78357  ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
78358  pParser->metadataCount += 1;
78359  } else {
78360  bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);
78361  if (bytesRead == pChunkHeader->sizeInBytes) {
78362  pParser->metadataCursor += 1;
78363  } else {
78364  }
78365  }
78366  } else {
78367  }
78368  } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) {
78369  ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid;
78370  while (bytesRead < pChunkHeader->sizeInBytes) {
78371  ma_uint8 subchunkId[4];
78372  ma_uint8 subchunkSizeBuffer[4];
78373  ma_uint64 subchunkDataSize;
78374  ma_uint64 subchunkBytesRead = 0;
78375  ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);
78376  if (bytesJustRead != sizeof(subchunkId)) {
78377  break;
78378  }
78379  if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) {
78380  listType = ma_dr_wav_metadata_location_inside_adtl_list;
78381  continue;
78382  } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) {
78383  listType = ma_dr_wav_metadata_location_inside_info_list;
78384  continue;
78385  }
78386  bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);
78387  if (bytesJustRead != sizeof(subchunkSizeBuffer)) {
78388  break;
78389  }
78390  subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer);
78391  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")) {
78392  if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) {
78393  ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
78394  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78395  pParser->metadataCount += 1;
78396  ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);
78397  } else {
78398  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);
78399  if (subchunkBytesRead == subchunkDataSize) {
78400  pParser->metadataCursor += 1;
78401  } else {
78402  }
78403  }
78404  } else {
78405  }
78406  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) {
78407  if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) {
78408  ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
78409  if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
78410  pParser->metadataCount += 1;
78411  ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);
78412  } else {
78413  subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);
78414  if (subchunkBytesRead == subchunkDataSize) {
78415  pParser->metadataCursor += 1;
78416  } else {
78417  }
78418  }
78419  } else {
78420  }
78421  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) {
78422  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software);
78423  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) {
78424  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright);
78425  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) {
78426  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title);
78427  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) {
78428  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist);
78429  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) {
78430  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment);
78431  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) {
78432  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date);
78433  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) {
78434  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre);
78435  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) {
78436  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album);
78437  } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) {
78438  subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber);
78439  } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) {
78440  subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
78441  }
78442  bytesRead += subchunkBytesRead;
78443  MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize);
78444  if (subchunkBytesRead < subchunkDataSize) {
78445  ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
78446  if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) {
78447  break;
78448  }
78449  bytesRead += bytesToSeek;
78450  }
78451  if ((subchunkDataSize % 2) == 1) {
78452  if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) {
78453  break;
78454  }
78455  bytesRead += 1;
78456  }
78457  }
78458  } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) {
78459  bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level);
78460  }
78461  return bytesRead;
78462 }
78463 MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav)
78464 {
78465  ma_uint32 bytesPerFrame;
78466  if ((pWav->bitsPerSample & 0x7) == 0) {
78467  bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
78468  } else {
78469  bytesPerFrame = pWav->fmt.blockAlign;
78470  }
78471  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
78472  if (bytesPerFrame != pWav->fmt.channels) {
78473  return 0;
78474  }
78475  }
78476  return bytesPerFrame;
78477 }
78478 MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT)
78479 {
78480  if (pFMT == NULL) {
78481  return 0;
78482  }
78483  if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) {
78484  return pFMT->formatTag;
78485  } else {
78486  return ma_dr_wav_bytes_to_u16(pFMT->subFormat);
78487  }
78488 }
78489 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)
78490 {
78491  if (pWav == NULL || onRead == NULL || onSeek == NULL) {
78492  return MA_FALSE;
78493  }
78494  MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav));
78495  pWav->onRead = onRead;
78496  pWav->onSeek = onSeek;
78497  pWav->pUserData = pReadSeekUserData;
78498  pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
78499  if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
78500  return MA_FALSE;
78501  }
78502  return MA_TRUE;
78503 }
78504 MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags)
78505 {
78506  ma_result result;
78507  ma_uint64 cursor;
78508  ma_bool32 sequential;
78509  ma_uint8 riff[4];
78510  ma_dr_wav_fmt fmt;
78511  unsigned short translatedFormatTag;
78512  ma_uint64 dataChunkSize = 0;
78513  ma_uint64 sampleCountFromFactChunk = 0;
78514  ma_uint64 metadataStartPos;
78515  ma_dr_wav__metadata_parser metadataParser;
78516  ma_bool8 isProcessingMetadata = MA_FALSE;
78517  ma_bool8 foundChunk_fmt = MA_FALSE;
78518  ma_bool8 foundChunk_data = MA_FALSE;
78519  ma_bool8 isAIFCFormType = MA_FALSE;
78520  ma_uint64 aiffFrameCount = 0;
78521  cursor = 0;
78522  sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0;
78523  MA_DR_WAV_ZERO_OBJECT(&fmt);
78524  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
78525  return MA_FALSE;
78526  }
78527  if (ma_dr_wav_fourcc_equal(riff, "RIFF")) {
78528  pWav->container = ma_dr_wav_container_riff;
78529  } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) {
78530  pWav->container = ma_dr_wav_container_rifx;
78531  } else if (ma_dr_wav_fourcc_equal(riff, "riff")) {
78532  int i;
78533  ma_uint8 riff2[12];
78534  pWav->container = ma_dr_wav_container_w64;
78535  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
78536  return MA_FALSE;
78537  }
78538  for (i = 0; i < 12; ++i) {
78539  if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) {
78540  return MA_FALSE;
78541  }
78542  }
78543  } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) {
78544  pWav->container = ma_dr_wav_container_rf64;
78545  } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) {
78546  pWav->container = ma_dr_wav_container_aiff;
78547  } else {
78548  return MA_FALSE;
78549  }
78550  if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) {
78551  ma_uint8 chunkSizeBytes[4];
78552  ma_uint8 wave[4];
78553  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
78554  return MA_FALSE;
78555  }
78556  if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {
78557  if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
78558  return MA_FALSE;
78559  }
78560  } else if (pWav->container == ma_dr_wav_container_rf64) {
78561  if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
78562  return MA_FALSE;
78563  }
78564  } else {
78565  return MA_FALSE;
78566  }
78567  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
78568  return MA_FALSE;
78569  }
78570  if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) {
78571  return MA_FALSE;
78572  }
78573  } else if (pWav->container == ma_dr_wav_container_w64) {
78574  ma_uint8 chunkSizeBytes[8];
78575  ma_uint8 wave[16];
78576  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
78577  return MA_FALSE;
78578  }
78579  if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) {
78580  return MA_FALSE;
78581  }
78582  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
78583  return MA_FALSE;
78584  }
78585  if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) {
78586  return MA_FALSE;
78587  }
78588  } else if (pWav->container == ma_dr_wav_container_aiff) {
78589  ma_uint8 chunkSizeBytes[4];
78590  ma_uint8 aiff[4];
78591  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
78592  return MA_FALSE;
78593  }
78594  if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) {
78595  return MA_FALSE;
78596  }
78597  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) {
78598  return MA_FALSE;
78599  }
78600  if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) {
78601  isAIFCFormType = MA_FALSE;
78602  } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) {
78603  isAIFCFormType = MA_TRUE;
78604  } else {
78605  return MA_FALSE;
78606  }
78607  } else {
78608  return MA_FALSE;
78609  }
78610  if (pWav->container == ma_dr_wav_container_rf64) {
78611  ma_uint8 sizeBytes[8];
78612  ma_uint64 bytesRemainingInChunk;
78613  ma_dr_wav_chunk_header header;
78614  result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
78615  if (result != MA_SUCCESS) {
78616  return MA_FALSE;
78617  }
78618  if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) {
78619  return MA_FALSE;
78620  }
78621  bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
78622  if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
78623  return MA_FALSE;
78624  }
78625  bytesRemainingInChunk -= 8;
78626  cursor += 8;
78627  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
78628  return MA_FALSE;
78629  }
78630  bytesRemainingInChunk -= 8;
78631  dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes);
78632  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
78633  return MA_FALSE;
78634  }
78635  bytesRemainingInChunk -= 8;
78636  sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes);
78637  if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
78638  return MA_FALSE;
78639  }
78640  cursor += bytesRemainingInChunk;
78641  }
78642  metadataStartPos = cursor;
78643  isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0);
78644  if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) {
78645  isProcessingMetadata = MA_FALSE;
78646  }
78647  MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));
78648  if (isProcessingMetadata) {
78649  metadataParser.onRead = pWav->onRead;
78650  metadataParser.onSeek = pWav->onSeek;
78651  metadataParser.pReadSeekUserData = pWav->pUserData;
78652  metadataParser.stage = ma_dr_wav__metadata_parser_stage_count;
78653  }
78654  for (;;) {
78655  ma_dr_wav_chunk_header header;
78656  ma_uint64 chunkSize;
78657  result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
78658  if (result != MA_SUCCESS) {
78659  break;
78660  }
78661  chunkSize = header.sizeInBytes;
78662  if (!sequential && onChunk != NULL) {
78663  ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
78664  if (callbackBytesRead > 0) {
78665  if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {
78666  return MA_FALSE;
78667  }
78668  }
78669  }
78670  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 ")) ||
78671  ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) {
78672  ma_uint8 fmtData[16];
78673  foundChunk_fmt = MA_TRUE;
78674  if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) {
78675  return MA_FALSE;
78676  }
78677  cursor += sizeof(fmtData);
78678  fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container);
78679  fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container);
78680  fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container);
78681  fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container);
78682  fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container);
78683  fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container);
78684  fmt.extendedSize = 0;
78685  fmt.validBitsPerSample = 0;
78686  fmt.channelMask = 0;
78687  MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat));
78688  if (header.sizeInBytes > 16) {
78689  ma_uint8 fmt_cbSize[2];
78690  int bytesReadSoFar = 0;
78691  if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
78692  return MA_FALSE;
78693  }
78694  cursor += sizeof(fmt_cbSize);
78695  bytesReadSoFar = 18;
78696  fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container);
78697  if (fmt.extendedSize > 0) {
78698  if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
78699  if (fmt.extendedSize != 22) {
78700  return MA_FALSE;
78701  }
78702  }
78703  if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
78704  ma_uint8 fmtext[22];
78705  if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) {
78706  return MA_FALSE;
78707  }
78708  fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container);
78709  fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container);
78710  ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat);
78711  } else {
78712  if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) {
78713  return MA_FALSE;
78714  }
78715  }
78716  cursor += fmt.extendedSize;
78717  bytesReadSoFar += fmt.extendedSize;
78718  }
78719  if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) {
78720  return MA_FALSE;
78721  }
78722  cursor += (header.sizeInBytes - bytesReadSoFar);
78723  }
78724  if (header.paddingSize > 0) {
78725  if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) {
78726  break;
78727  }
78728  cursor += header.paddingSize;
78729  }
78730  continue;
78731  }
78732  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")) ||
78733  ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) {
78734  foundChunk_data = MA_TRUE;
78735  pWav->dataChunkDataPos = cursor;
78736  if (pWav->container != ma_dr_wav_container_rf64) {
78737  dataChunkSize = chunkSize;
78738  }
78739  if (sequential || !isProcessingMetadata) {
78740  break;
78741  } else {
78742  chunkSize += header.paddingSize;
78743  if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
78744  break;
78745  }
78746  cursor += chunkSize;
78747  continue;
78748  }
78749  }
78750  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")) ||
78751  ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) {
78752  if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {
78753  ma_uint8 sampleCount[4];
78754  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
78755  return MA_FALSE;
78756  }
78757  chunkSize -= 4;
78758  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
78759  sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container);
78760  } else {
78761  sampleCountFromFactChunk = 0;
78762  }
78763  } else if (pWav->container == ma_dr_wav_container_w64) {
78764  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
78765  return MA_FALSE;
78766  }
78767  chunkSize -= 8;
78768  } else if (pWav->container == ma_dr_wav_container_rf64) {
78769  }
78770  chunkSize += header.paddingSize;
78771  if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
78772  break;
78773  }
78774  cursor += chunkSize;
78775  continue;
78776  }
78777  if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) {
78778  ma_uint8 commData[24];
78779  ma_uint32 commDataBytesToRead;
78780  ma_uint16 channels;
78781  ma_uint32 frameCount;
78782  ma_uint16 sampleSizeInBits;
78783  ma_int64 sampleRate;
78784  ma_uint16 compressionFormat;
78785  foundChunk_fmt = MA_TRUE;
78786  if (isAIFCFormType) {
78787  commDataBytesToRead = 24;
78788  if (header.sizeInBytes < commDataBytesToRead) {
78789  return MA_FALSE;
78790  }
78791  } else {
78792  commDataBytesToRead = 18;
78793  if (header.sizeInBytes != commDataBytesToRead) {
78794  return MA_FALSE;
78795  }
78796  }
78797  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) {
78798  return MA_FALSE;
78799  }
78800  channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container);
78801  frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container);
78802  sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container);
78803  sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8);
78804  if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) {
78805  return MA_FALSE;
78806  }
78807  if (isAIFCFormType) {
78808  const ma_uint8* type = commData + 18;
78809  if (ma_dr_wav_fourcc_equal(type, "NONE")) {
78810  compressionFormat = MA_DR_WAVE_FORMAT_PCM;
78811  } else if (ma_dr_wav_fourcc_equal(type, "raw ")) {
78812  compressionFormat = MA_DR_WAVE_FORMAT_PCM;
78813  if (sampleSizeInBits == 8) {
78814  pWav->aiff.isUnsigned = MA_TRUE;
78815  }
78816  } else if (ma_dr_wav_fourcc_equal(type, "sowt")) {
78817  compressionFormat = MA_DR_WAVE_FORMAT_PCM;
78818  pWav->aiff.isLE = MA_TRUE;
78819  } 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")) {
78820  compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT;
78821  } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) {
78822  compressionFormat = MA_DR_WAVE_FORMAT_ALAW;
78823  } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) {
78824  compressionFormat = MA_DR_WAVE_FORMAT_MULAW;
78825  } else if (ma_dr_wav_fourcc_equal(type, "ima4")) {
78826  compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM;
78827  sampleSizeInBits = 4;
78828  return MA_FALSE;
78829  } else {
78830  return MA_FALSE;
78831  }
78832  } else {
78833  compressionFormat = MA_DR_WAVE_FORMAT_PCM;
78834  }
78835  aiffFrameCount = frameCount;
78836  fmt.formatTag = compressionFormat;
78837  fmt.channels = channels;
78838  fmt.sampleRate = (ma_uint32)sampleRate;
78839  fmt.bitsPerSample = sampleSizeInBits;
78840  fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8);
78841  fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate;
78842  if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
78843  fmt.blockAlign = 34 * fmt.channels;
78844  }
78845  if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) {
78846  if (fmt.bitsPerSample > 8) {
78847  fmt.bitsPerSample = 8;
78848  fmt.blockAlign = fmt.channels;
78849  }
78850  }
78851  fmt.bitsPerSample += (fmt.bitsPerSample & 7);
78852  if (isAIFCFormType) {
78853  if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) {
78854  return MA_FALSE;
78855  }
78856  cursor += (chunkSize - commDataBytesToRead);
78857  }
78858  continue;
78859  }
78860  if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) {
78861  ma_uint8 offsetAndBlockSizeData[8];
78862  ma_uint32 offset;
78863  foundChunk_data = MA_TRUE;
78864  if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) {
78865  return MA_FALSE;
78866  }
78867  offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container);
78868  if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) {
78869  return MA_FALSE;
78870  }
78871  cursor += offset;
78872  pWav->dataChunkDataPos = cursor;
78873  dataChunkSize = chunkSize;
78874  if (sequential || !isProcessingMetadata) {
78875  break;
78876  } else {
78877  if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
78878  break;
78879  }
78880  cursor += chunkSize;
78881  continue;
78882  }
78883  }
78884  if (isProcessingMetadata) {
78885  ma_uint64 metadataBytesRead;
78886  metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
78887  MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes);
78888  if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {
78889  break;
78890  }
78891  }
78892  chunkSize += header.paddingSize;
78893  if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
78894  break;
78895  }
78896  cursor += chunkSize;
78897  }
78898  if (!foundChunk_fmt || !foundChunk_data) {
78899  return MA_FALSE;
78900  }
78901  if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) ||
78902  (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) ||
78903  (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) ||
78904  fmt.blockAlign == 0) {
78905  return MA_FALSE;
78906  }
78907  translatedFormatTag = fmt.formatTag;
78908  if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
78909  translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container);
78910  }
78911  if (!sequential) {
78912  if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
78913  return MA_FALSE;
78914  }
78915  cursor = pWav->dataChunkDataPos;
78916  }
78917  if (isProcessingMetadata && metadataParser.metadataCount > 0) {
78918  if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) {
78919  return MA_FALSE;
78920  }
78921  result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);
78922  if (result != MA_SUCCESS) {
78923  return MA_FALSE;
78924  }
78925  metadataParser.stage = ma_dr_wav__metadata_parser_stage_read;
78926  for (;;) {
78927  ma_dr_wav_chunk_header header;
78928  ma_uint64 metadataBytesRead;
78929  result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
78930  if (result != MA_SUCCESS) {
78931  break;
78932  }
78933  metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
78934  if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) {
78935  ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks);
78936  return MA_FALSE;
78937  }
78938  }
78939  pWav->pMetadata = metadataParser.pMetadata;
78940  pWav->metadataCount = metadataParser.metadataCount;
78941  }
78942  if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) {
78943  dataChunkSize = 0;
78944  for (;;) {
78945  ma_uint8 temp[4096];
78946  size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp));
78947  dataChunkSize += bytesRead;
78948  if (bytesRead < sizeof(temp)) {
78949  break;
78950  }
78951  }
78952  }
78953  if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) {
78954  ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
78955  return MA_FALSE;
78956  }
78957  pWav->fmt = fmt;
78958  pWav->sampleRate = fmt.sampleRate;
78959  pWav->channels = fmt.channels;
78960  pWav->bitsPerSample = fmt.bitsPerSample;
78961  pWav->bytesRemaining = dataChunkSize;
78962  pWav->translatedFormatTag = translatedFormatTag;
78963  pWav->dataChunkDataSize = dataChunkSize;
78964  if (sampleCountFromFactChunk != 0) {
78965  pWav->totalPCMFrameCount = sampleCountFromFactChunk;
78966  } else if (aiffFrameCount != 0) {
78967  pWav->totalPCMFrameCount = aiffFrameCount;
78968  } else {
78969  ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
78970  if (bytesPerFrame == 0) {
78971  ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
78972  return MA_FALSE;
78973  }
78974  pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame;
78975  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
78976  ma_uint64 totalBlockHeaderSizeInBytes;
78977  ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
78978  if ((blockCount * fmt.blockAlign) < dataChunkSize) {
78979  blockCount += 1;
78980  }
78981  totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
78982  pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
78983  }
78984  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
78985  ma_uint64 totalBlockHeaderSizeInBytes;
78986  ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
78987  if ((blockCount * fmt.blockAlign) < dataChunkSize) {
78988  blockCount += 1;
78989  }
78990  totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
78991  pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
78992  pWav->totalPCMFrameCount += blockCount;
78993  }
78994  }
78995  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
78996  if (pWav->channels > 2) {
78997  ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
78998  return MA_FALSE;
78999  }
79000  }
79001  if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) {
79002  ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
79003  return MA_FALSE;
79004  }
79005 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
79006  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
79007  ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
79008  pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;
79009  }
79010  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
79011  ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
79012  pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
79013  }
79014 #endif
79015  return MA_TRUE;
79016 }
79017 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)
79018 {
79019  return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
79020 }
79021 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)
79022 {
79023  if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
79024  return MA_FALSE;
79025  }
79026  return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
79027 }
79028 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)
79029 {
79030  if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
79031  return MA_FALSE;
79032  }
79033  return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA);
79034 }
79035 MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav)
79036 {
79037  ma_dr_wav_metadata *result = pWav->pMetadata;
79038  pWav->pMetadata = NULL;
79039  pWav->metadataCount = 0;
79040  return result;
79041 }
79042 MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize)
79043 {
79044  MA_DR_WAV_ASSERT(pWav != NULL);
79045  MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
79046  return pWav->onWrite(pWav->pUserData, pData, dataSize);
79047 }
79048 MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte)
79049 {
79050  MA_DR_WAV_ASSERT(pWav != NULL);
79051  MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
79052  return pWav->onWrite(pWav->pUserData, &byte, 1);
79053 }
79054 MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value)
79055 {
79056  MA_DR_WAV_ASSERT(pWav != NULL);
79057  MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
79058  if (!ma_dr_wav__is_little_endian()) {
79059  value = ma_dr_wav__bswap16(value);
79060  }
79061  return ma_dr_wav__write(pWav, &value, 2);
79062 }
79063 MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value)
79064 {
79065  MA_DR_WAV_ASSERT(pWav != NULL);
79066  MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
79067  if (!ma_dr_wav__is_little_endian()) {
79068  value = ma_dr_wav__bswap32(value);
79069  }
79070  return ma_dr_wav__write(pWav, &value, 4);
79071 }
79072 MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value)
79073 {
79074  MA_DR_WAV_ASSERT(pWav != NULL);
79075  MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
79076  if (!ma_dr_wav__is_little_endian()) {
79077  value = ma_dr_wav__bswap64(value);
79078  }
79079  return ma_dr_wav__write(pWav, &value, 8);
79080 }
79081 MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value)
79082 {
79083  union {
79084  ma_uint32 u32;
79085  float f32;
79086  } u;
79087  MA_DR_WAV_ASSERT(pWav != NULL);
79088  MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
79089  u.f32 = value;
79090  if (!ma_dr_wav__is_little_endian()) {
79091  u.u32 = ma_dr_wav__bswap32(u.u32);
79092  }
79093  return ma_dr_wav__write(pWav, &u.u32, 4);
79094 }
79095 MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize)
79096 {
79097  if (pWav == NULL) {
79098  return dataSize;
79099  }
79100  return ma_dr_wav__write(pWav, pData, dataSize);
79101 }
79102 MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte)
79103 {
79104  if (pWav == NULL) {
79105  return 1;
79106  }
79107  return ma_dr_wav__write_byte(pWav, byte);
79108 }
79109 MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value)
79110 {
79111  if (pWav == NULL) {
79112  return 2;
79113  }
79114  return ma_dr_wav__write_u16ne_to_le(pWav, value);
79115 }
79116 MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value)
79117 {
79118  if (pWav == NULL) {
79119  return 4;
79120  }
79121  return ma_dr_wav__write_u32ne_to_le(pWav, value);
79122 }
79123 #if 0
79124 MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value)
79125 {
79126  if (pWav == NULL) {
79127  return 8;
79128  }
79129  return ma_dr_wav__write_u64ne_to_le(pWav, value);
79130 }
79131 #endif
79132 MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value)
79133 {
79134  if (pWav == NULL) {
79135  return 4;
79136  }
79137  return ma_dr_wav__write_f32ne_to_le(pWav, value);
79138 }
79139 MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize)
79140 {
79141  size_t len;
79142  if (pWav == NULL) {
79143  return bufFixedSize;
79144  }
79145  len = ma_dr_wav__strlen_clamped(str, bufFixedSize);
79146  ma_dr_wav__write_or_count(pWav, str, len);
79147  if (len < bufFixedSize) {
79148  size_t i;
79149  for (i = 0; i < bufFixedSize - len; ++i) {
79150  ma_dr_wav__write_byte(pWav, 0);
79151  }
79152  }
79153  return bufFixedSize;
79154 }
79155 MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount)
79156 {
79157  size_t bytesWritten = 0;
79158  ma_bool32 hasListAdtl = MA_FALSE;
79159  ma_bool32 hasListInfo = MA_FALSE;
79160  ma_uint32 iMetadata;
79161  if (pMetadatas == NULL || metadataCount == 0) {
79162  return 0;
79163  }
79164  for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
79165  ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
79166  ma_uint32 chunkSize = 0;
79167  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)) {
79168  hasListInfo = MA_TRUE;
79169  }
79170  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)) {
79171  hasListAdtl = MA_TRUE;
79172  }
79173  switch (pMetadata->type) {
79174  case ma_dr_wav_metadata_type_smpl:
79175  {
79176  ma_uint32 iLoop;
79177  chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;
79178  bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4);
79179  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
79180  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);
79181  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);
79182  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);
79183  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);
79184  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);
79185  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);
79186  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);
79187  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);
79188  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
79189  for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
79190  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
79191  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
79192  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset);
79193  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset);
79194  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
79195  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
79196  }
79197  if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
79198  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
79199  }
79200  } break;
79201  case ma_dr_wav_metadata_type_inst:
79202  {
79203  chunkSize = MA_DR_WAV_INST_BYTES;
79204  bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4);
79205  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
79206  bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);
79207  bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);
79208  bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);
79209  bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);
79210  bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);
79211  bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);
79212  bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);
79213  } break;
79214  case ma_dr_wav_metadata_type_cue:
79215  {
79216  ma_uint32 iCuePoint;
79217  chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;
79218  bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4);
79219  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
79220  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);
79221  for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
79222  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);
79223  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);
79224  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
79225  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
79226  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
79227  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset);
79228  }
79229  } break;
79230  case ma_dr_wav_metadata_type_acid:
79231  {
79232  chunkSize = MA_DR_WAV_ACID_BYTES;
79233  bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4);
79234  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
79235  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);
79236  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);
79237  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);
79238  bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);
79239  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);
79240  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);
79241  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);
79242  bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);
79243  } break;
79244  case ma_dr_wav_metadata_type_bext:
79245  {
79246  char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES];
79247  ma_uint32 timeReferenceLow;
79248  ma_uint32 timeReferenceHigh;
79249  chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;
79250  bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4);
79251  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
79252  bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES);
79253  bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);
79254  bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);
79255  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
79256  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
79257  timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);
79258  timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32);
79259  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);
79260  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);
79261  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);
79262  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES);
79263  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);
79264  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);
79265  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
79266  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
79267  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
79268  MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));
79269  bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
79270  if (pMetadata->data.bext.codingHistorySize > 0) {
79271  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
79272  }
79273  } break;
79274  case ma_dr_wav_metadata_type_unknown:
79275  {
79276  if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) {
79277  chunkSize = pMetadata->data.unknown.dataSizeInBytes;
79278  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
79279  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
79280  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);
79281  }
79282  } break;
79283  default: break;
79284  }
79285  if ((chunkSize % 2) != 0) {
79286  bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
79287  }
79288  }
79289  if (hasListInfo) {
79290  ma_uint32 chunkSize = 4;
79291  for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
79292  ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
79293  if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) {
79294  chunkSize += 8;
79295  chunkSize += pMetadata->data.infoText.stringLength + 1;
79296  } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) {
79297  chunkSize += 8;
79298  chunkSize += pMetadata->data.unknown.dataSizeInBytes;
79299  }
79300  if ((chunkSize % 2) != 0) {
79301  chunkSize += 1;
79302  }
79303  }
79304  bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4);
79305  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
79306  bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4);
79307  for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
79308  ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
79309  ma_uint32 subchunkSize = 0;
79310  if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) {
79311  const char* pID = NULL;
79312  switch (pMetadata->type) {
79313  case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break;
79314  case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break;
79315  case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break;
79316  case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break;
79317  case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break;
79318  case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break;
79319  case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break;
79320  case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break;
79321  case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
79322  default: break;
79323  }
79324  MA_DR_WAV_ASSERT(pID != NULL);
79325  if (pMetadata->data.infoText.stringLength) {
79326  subchunkSize = pMetadata->data.infoText.stringLength + 1;
79327  bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4);
79328  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
79329  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);
79330  bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
79331  }
79332  } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) {
79333  if (pMetadata->data.unknown.dataSizeInBytes) {
79334  subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
79335  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
79336  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);
79337  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
79338  }
79339  }
79340  if ((subchunkSize % 2) != 0) {
79341  bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
79342  }
79343  }
79344  }
79345  if (hasListAdtl) {
79346  ma_uint32 chunkSize = 4;
79347  for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
79348  ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
79349  switch (pMetadata->type)
79350  {
79351  case ma_dr_wav_metadata_type_list_label:
79352  case ma_dr_wav_metadata_type_list_note:
79353  {
79354  chunkSize += 8;
79355  chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
79356  if (pMetadata->data.labelOrNote.stringLength > 0) {
79357  chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
79358  }
79359  } break;
79360  case ma_dr_wav_metadata_type_list_labelled_cue_region:
79361  {
79362  chunkSize += 8;
79363  chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
79364  if (pMetadata->data.labelledCueRegion.stringLength > 0) {
79365  chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
79366  }
79367  } break;
79368  case ma_dr_wav_metadata_type_unknown:
79369  {
79370  if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) {
79371  chunkSize += 8;
79372  chunkSize += pMetadata->data.unknown.dataSizeInBytes;
79373  }
79374  } break;
79375  default: break;
79376  }
79377  if ((chunkSize % 2) != 0) {
79378  chunkSize += 1;
79379  }
79380  }
79381  bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4);
79382  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
79383  bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4);
79384  for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
79385  ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
79386  ma_uint32 subchunkSize = 0;
79387  switch (pMetadata->type)
79388  {
79389  case ma_dr_wav_metadata_type_list_label:
79390  case ma_dr_wav_metadata_type_list_note:
79391  {
79392  if (pMetadata->data.labelOrNote.stringLength > 0) {
79393  const char *pID = NULL;
79394  if (pMetadata->type == ma_dr_wav_metadata_type_list_label) {
79395  pID = "labl";
79396  }
79397  else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) {
79398  pID = "note";
79399  }
79400  MA_DR_WAV_ASSERT(pID != NULL);
79401  MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
79402  subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
79403  bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4);
79404  subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;
79405  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
79406  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);
79407  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);
79408  bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
79409  }
79410  } break;
79411  case ma_dr_wav_metadata_type_list_labelled_cue_region:
79412  {
79413  subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
79414  bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4);
79415  if (pMetadata->data.labelledCueRegion.stringLength > 0) {
79416  subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
79417  }
79418  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
79419  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);
79420  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);
79421  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);
79422  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);
79423  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);
79424  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);
79425  bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);
79426  if (pMetadata->data.labelledCueRegion.stringLength > 0) {
79427  MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
79428  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);
79429  bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
79430  }
79431  } break;
79432  case ma_dr_wav_metadata_type_unknown:
79433  {
79434  if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) {
79435  subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
79436  MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL);
79437  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
79438  bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
79439  bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
79440  }
79441  } break;
79442  default: break;
79443  }
79444  if ((subchunkSize % 2) != 0) {
79445  bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
79446  }
79447  }
79448  }
79449  MA_DR_WAV_ASSERT((bytesWritten % 2) == 0);
79450  return bytesWritten;
79451 }
79452 MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)
79453 {
79454  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);
79455  if (chunkSize > 0xFFFFFFFFUL) {
79456  chunkSize = 0xFFFFFFFFUL;
79457  }
79458  return (ma_uint32)chunkSize;
79459 }
79460 MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize)
79461 {
79462  if (dataChunkSize <= 0xFFFFFFFFUL) {
79463  return (ma_uint32)dataChunkSize;
79464  } else {
79465  return 0xFFFFFFFFUL;
79466  }
79467 }
79468 MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize)
79469 {
79470  ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize);
79471  return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;
79472 }
79473 MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize)
79474 {
79475  return 24 + dataChunkSize;
79476 }
79477 MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata)
79478 {
79479  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);
79480  if (chunkSize > 0xFFFFFFFFUL) {
79481  chunkSize = 0xFFFFFFFFUL;
79482  }
79483  return chunkSize;
79484 }
79485 MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize)
79486 {
79487  return dataChunkSize;
79488 }
79489 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)
79490 {
79491  if (pWav == NULL || onWrite == NULL) {
79492  return MA_FALSE;
79493  }
79494  if (!isSequential && onSeek == NULL) {
79495  return MA_FALSE;
79496  }
79497  if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
79498  return MA_FALSE;
79499  }
79500  if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
79501  return MA_FALSE;
79502  }
79503  MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav));
79504  pWav->onWrite = onWrite;
79505  pWav->onSeek = onSeek;
79506  pWav->pUserData = pUserData;
79507  pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
79508  if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
79509  return MA_FALSE;
79510  }
79511  pWav->fmt.formatTag = (ma_uint16)pFormat->format;
79512  pWav->fmt.channels = (ma_uint16)pFormat->channels;
79513  pWav->fmt.sampleRate = pFormat->sampleRate;
79514  pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
79515  pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
79516  pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample;
79517  pWav->fmt.extendedSize = 0;
79518  pWav->isSequentialWrite = isSequential;
79519  return MA_TRUE;
79520 }
79521 MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount)
79522 {
79523  size_t runningPos = 0;
79524  ma_uint64 initialDataChunkSize = 0;
79525  ma_uint64 chunkSizeFMT;
79526  if (pWav->isSequentialWrite) {
79527  initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
79528  if (pFormat->container == ma_dr_wav_container_riff) {
79529  if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
79530  return MA_FALSE;
79531  }
79532  }
79533  }
79534  pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
79535  if (pFormat->container == ma_dr_wav_container_riff) {
79536  ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize;
79537  runningPos += ma_dr_wav__write(pWav, "RIFF", 4);
79538  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF);
79539  runningPos += ma_dr_wav__write(pWav, "WAVE", 4);
79540  } else if (pFormat->container == ma_dr_wav_container_w64) {
79541  ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;
79542  runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16);
79543  runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF);
79544  runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16);
79545  } else if (pFormat->container == ma_dr_wav_container_rf64) {
79546  runningPos += ma_dr_wav__write(pWav, "RF64", 4);
79547  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
79548  runningPos += ma_dr_wav__write(pWav, "WAVE", 4);
79549  } else {
79550  return MA_FALSE;
79551  }
79552  if (pFormat->container == ma_dr_wav_container_rf64) {
79553  ma_uint32 initialds64ChunkSize = 28;
79554  ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;
79555  runningPos += ma_dr_wav__write(pWav, "ds64", 4);
79556  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize);
79557  runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize);
79558  runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize);
79559  runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount);
79560  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0);
79561  }
79562  if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) {
79563  chunkSizeFMT = 16;
79564  runningPos += ma_dr_wav__write(pWav, "fmt ", 4);
79565  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT);
79566  } else if (pFormat->container == ma_dr_wav_container_w64) {
79567  chunkSizeFMT = 40;
79568  runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16);
79569  runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT);
79570  }
79571  runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
79572  runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels);
79573  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
79574  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
79575  runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
79576  runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
79577  if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) {
79578  runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);
79579  }
79580  pWav->dataChunkDataPos = runningPos;
79581  if (pFormat->container == ma_dr_wav_container_riff) {
79582  ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize;
79583  runningPos += ma_dr_wav__write(pWav, "data", 4);
79584  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA);
79585  } else if (pFormat->container == ma_dr_wav_container_w64) {
79586  ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize;
79587  runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16);
79588  runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA);
79589  } else if (pFormat->container == ma_dr_wav_container_rf64) {
79590  runningPos += ma_dr_wav__write(pWav, "data", 4);
79591  runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
79592  }
79593  pWav->container = pFormat->container;
79594  pWav->channels = (ma_uint16)pFormat->channels;
79595  pWav->sampleRate = pFormat->sampleRate;
79596  pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample;
79597  pWav->translatedFormatTag = (ma_uint16)pFormat->format;
79598  pWav->dataChunkDataPos = runningPos;
79599  return MA_TRUE;
79600 }
79601 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)
79602 {
79603  if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
79604  return MA_FALSE;
79605  }
79606  return ma_dr_wav_init_write__internal(pWav, pFormat, 0);
79607 }
79608 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)
79609 {
79610  if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
79611  return MA_FALSE;
79612  }
79613  return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
79614 }
79615 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)
79616 {
79617  if (pFormat == NULL) {
79618  return MA_FALSE;
79619  }
79620  return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
79621 }
79622 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)
79623 {
79624  if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
79625  return MA_FALSE;
79626  }
79627  pWav->pMetadata = pMetadata;
79628  pWav->metadataCount = metadataCount;
79629  return ma_dr_wav_init_write__internal(pWav, pFormat, 0);
79630 }
79631 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)
79632 {
79633  ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);
79634  ma_uint64 riffChunkSizeBytes;
79635  ma_uint64 fileSizeBytes = 0;
79636  if (pFormat->container == ma_dr_wav_container_riff) {
79637  riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);
79638  fileSizeBytes = (8 + riffChunkSizeBytes);
79639  } else if (pFormat->container == ma_dr_wav_container_w64) {
79640  riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes);
79641  fileSizeBytes = riffChunkSizeBytes;
79642  } else if (pFormat->container == ma_dr_wav_container_rf64) {
79643  riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);
79644  fileSizeBytes = (8 + riffChunkSizeBytes);
79645  }
79646  return fileSizeBytes;
79647 }
79648 #ifndef MA_DR_WAV_NO_STDIO
79649 MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
79650 {
79651  return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
79652 }
79653 MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
79654 {
79655  return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
79656 }
79657 MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
79658 {
79659  return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
79660 }
79661 MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks)
79662 {
79663  return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
79664 }
79665 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)
79666 {
79667  ma_bool32 result;
79668  result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
79669  if (result != MA_TRUE) {
79670  fclose(pFile);
79671  return result;
79672  }
79673  result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
79674  if (result != MA_TRUE) {
79675  fclose(pFile);
79676  return result;
79677  }
79678  return MA_TRUE;
79679 }
79680 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)
79681 {
79682  FILE* pFile;
79683  if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) {
79684  return MA_FALSE;
79685  }
79686  return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
79687 }
79688 #ifndef MA_DR_WAV_NO_WCHAR
79689 MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks)
79690 {
79691  return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
79692 }
79693 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)
79694 {
79695  FILE* pFile;
79696  if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
79697  return MA_FALSE;
79698  }
79699  return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
79700 }
79701 #endif
79702 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)
79703 {
79704  FILE* pFile;
79705  if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) {
79706  return MA_FALSE;
79707  }
79708  return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks);
79709 }
79710 #ifndef MA_DR_WAV_NO_WCHAR
79711 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)
79712 {
79713  FILE* pFile;
79714  if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
79715  return MA_FALSE;
79716  }
79717  return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks);
79718 }
79719 #endif
79720 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)
79721 {
79722  ma_bool32 result;
79723  result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
79724  if (result != MA_TRUE) {
79725  fclose(pFile);
79726  return result;
79727  }
79728  result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
79729  if (result != MA_TRUE) {
79730  fclose(pFile);
79731  return result;
79732  }
79733  return MA_TRUE;
79734 }
79735 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)
79736 {
79737  FILE* pFile;
79738  if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) {
79739  return MA_FALSE;
79740  }
79741  return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
79742 }
79743 #ifndef MA_DR_WAV_NO_WCHAR
79744 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)
79745 {
79746  FILE* pFile;
79747  if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) {
79748  return MA_FALSE;
79749  }
79750  return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
79751 }
79752 #endif
79753 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)
79754 {
79755  return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks);
79756 }
79757 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)
79758 {
79759  return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
79760 }
79761 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)
79762 {
79763  if (pFormat == NULL) {
79764  return MA_FALSE;
79765  }
79766  return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
79767 }
79768 #ifndef MA_DR_WAV_NO_WCHAR
79769 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)
79770 {
79771  return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks);
79772 }
79773 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)
79774 {
79775  return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
79776 }
79777 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)
79778 {
79779  if (pFormat == NULL) {
79780  return MA_FALSE;
79781  }
79782  return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
79783 }
79784 #endif
79785 #endif
79786 MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
79787 {
79788  ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
79789  size_t bytesRemaining;
79790  MA_DR_WAV_ASSERT(pWav != NULL);
79791  MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
79792  bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
79793  if (bytesToRead > bytesRemaining) {
79794  bytesToRead = bytesRemaining;
79795  }
79796  if (bytesToRead > 0) {
79797  MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
79798  pWav->memoryStream.currentReadPos += bytesToRead;
79799  }
79800  return bytesToRead;
79801 }
79802 MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
79803 {
79804  ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
79805  MA_DR_WAV_ASSERT(pWav != NULL);
79806  if (origin == ma_dr_wav_seek_origin_current) {
79807  if (offset > 0) {
79808  if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
79809  return MA_FALSE;
79810  }
79811  } else {
79812  if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
79813  return MA_FALSE;
79814  }
79815  }
79816  pWav->memoryStream.currentReadPos += offset;
79817  } else {
79818  if ((ma_uint32)offset <= pWav->memoryStream.dataSize) {
79819  pWav->memoryStream.currentReadPos = offset;
79820  } else {
79821  return MA_FALSE;
79822  }
79823  }
79824  return MA_TRUE;
79825 }
79826 MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
79827 {
79828  ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
79829  size_t bytesRemaining;
79830  MA_DR_WAV_ASSERT(pWav != NULL);
79831  MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
79832  bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
79833  if (bytesRemaining < bytesToWrite) {
79834  void* pNewData;
79835  size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
79836  if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
79837  newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
79838  }
79839  pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
79840  if (pNewData == NULL) {
79841  return 0;
79842  }
79843  *pWav->memoryStreamWrite.ppData = pNewData;
79844  pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
79845  }
79846  MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
79847  pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
79848  if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
79849  pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
79850  }
79851  *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
79852  return bytesToWrite;
79853 }
79854 MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
79855 {
79856  ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
79857  MA_DR_WAV_ASSERT(pWav != NULL);
79858  if (origin == ma_dr_wav_seek_origin_current) {
79859  if (offset > 0) {
79860  if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
79861  offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos);
79862  }
79863  } else {
79864  if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
79865  offset = -(int)pWav->memoryStreamWrite.currentWritePos;
79866  }
79867  }
79868  pWav->memoryStreamWrite.currentWritePos += offset;
79869  } else {
79870  if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
79871  pWav->memoryStreamWrite.currentWritePos = offset;
79872  } else {
79873  pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize;
79874  }
79875  }
79876  return MA_TRUE;
79877 }
79878 MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
79879 {
79880  return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
79881 }
79882 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)
79883 {
79884  if (data == NULL || dataSize == 0) {
79885  return MA_FALSE;
79886  }
79887  if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) {
79888  return MA_FALSE;
79889  }
79890  pWav->memoryStream.data = (const ma_uint8*)data;
79891  pWav->memoryStream.dataSize = dataSize;
79892  pWav->memoryStream.currentReadPos = 0;
79893  return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
79894 }
79895 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)
79896 {
79897  if (data == NULL || dataSize == 0) {
79898  return MA_FALSE;
79899  }
79900  if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) {
79901  return MA_FALSE;
79902  }
79903  pWav->memoryStream.data = (const ma_uint8*)data;
79904  pWav->memoryStream.dataSize = dataSize;
79905  pWav->memoryStream.currentReadPos = 0;
79906  return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA);
79907 }
79908 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)
79909 {
79910  if (ppData == NULL || pDataSize == NULL) {
79911  return MA_FALSE;
79912  }
79913  *ppData = NULL;
79914  *pDataSize = 0;
79915  if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
79916  return MA_FALSE;
79917  }
79918  pWav->memoryStreamWrite.ppData = ppData;
79919  pWav->memoryStreamWrite.pDataSize = pDataSize;
79920  pWav->memoryStreamWrite.dataSize = 0;
79921  pWav->memoryStreamWrite.dataCapacity = 0;
79922  pWav->memoryStreamWrite.currentWritePos = 0;
79923  return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
79924 }
79925 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)
79926 {
79927  return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks);
79928 }
79929 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)
79930 {
79931  return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
79932 }
79933 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)
79934 {
79935  if (pFormat == NULL) {
79936  return MA_FALSE;
79937  }
79938  return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
79939 }
79940 MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav)
79941 {
79942  ma_result result = MA_SUCCESS;
79943  if (pWav == NULL) {
79944  return MA_INVALID_ARGS;
79945  }
79946  if (pWav->onWrite != NULL) {
79947  ma_uint32 paddingSize = 0;
79948  if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) {
79949  paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize);
79950  } else {
79951  paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize);
79952  }
79953  if (paddingSize > 0) {
79954  ma_uint64 paddingData = 0;
79955  ma_dr_wav__write(pWav, &paddingData, paddingSize);
79956  }
79957  if (pWav->onSeek && !pWav->isSequentialWrite) {
79958  if (pWav->container == ma_dr_wav_container_riff) {
79959  if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) {
79960  ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
79961  ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize);
79962  }
79963  if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) {
79964  ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize);
79965  ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize);
79966  }
79967  } else if (pWav->container == ma_dr_wav_container_w64) {
79968  if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) {
79969  ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize);
79970  ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize);
79971  }
79972  if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) {
79973  ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize);
79974  ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize);
79975  }
79976  } else if (pWav->container == ma_dr_wav_container_rf64) {
79977  int ds64BodyPos = 12 + 8;
79978  if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) {
79979  ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
79980  ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize);
79981  }
79982  if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) {
79983  ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize);
79984  ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize);
79985  }
79986  }
79987  }
79988  if (pWav->isSequentialWrite) {
79989  if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
79990  result = MA_INVALID_FILE;
79991  }
79992  }
79993  } else {
79994  ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
79995  }
79996 #ifndef MA_DR_WAV_NO_STDIO
79997  if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) {
79998  fclose((FILE*)pWav->pUserData);
79999  }
80000 #endif
80001  return result;
80002 }
80003 MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut)
80004 {
80005  size_t bytesRead;
80006  ma_uint32 bytesPerFrame;
80007  if (pWav == NULL || bytesToRead == 0) {
80008  return 0;
80009  }
80010  if (bytesToRead > pWav->bytesRemaining) {
80011  bytesToRead = (size_t)pWav->bytesRemaining;
80012  }
80013  if (bytesToRead == 0) {
80014  return 0;
80015  }
80016  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
80017  if (bytesPerFrame == 0) {
80018  return 0;
80019  }
80020  if (pBufferOut != NULL) {
80021  bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
80022  } else {
80023  bytesRead = 0;
80024  while (bytesRead < bytesToRead) {
80025  size_t bytesToSeek = (bytesToRead - bytesRead);
80026  if (bytesToSeek > 0x7FFFFFFF) {
80027  bytesToSeek = 0x7FFFFFFF;
80028  }
80029  if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) {
80030  break;
80031  }
80032  bytesRead += bytesToSeek;
80033  }
80034  while (bytesRead < bytesToRead) {
80035  ma_uint8 buffer[4096];
80036  size_t bytesSeeked;
80037  size_t bytesToSeek = (bytesToRead - bytesRead);
80038  if (bytesToSeek > sizeof(buffer)) {
80039  bytesToSeek = sizeof(buffer);
80040  }
80041  bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
80042  bytesRead += bytesSeeked;
80043  if (bytesSeeked < bytesToSeek) {
80044  break;
80045  }
80046  }
80047  }
80048  pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame;
80049  pWav->bytesRemaining -= bytesRead;
80050  return bytesRead;
80051 }
80052 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
80053 {
80054  ma_uint32 bytesPerFrame;
80055  ma_uint64 bytesToRead;
80056  ma_uint64 framesRemainingInFile;
80057  if (pWav == NULL || framesToRead == 0) {
80058  return 0;
80059  }
80060  if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
80061  return 0;
80062  }
80063  framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames;
80064  if (framesToRead > framesRemainingInFile) {
80065  framesToRead = framesRemainingInFile;
80066  }
80067  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
80068  if (bytesPerFrame == 0) {
80069  return 0;
80070  }
80071  bytesToRead = framesToRead * bytesPerFrame;
80072  if (bytesToRead > MA_SIZE_MAX) {
80073  bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame;
80074  }
80075  if (bytesToRead == 0) {
80076  return 0;
80077  }
80078  return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
80079 }
80080 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
80081 {
80082  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
80083  if (pBufferOut != NULL) {
80084  ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
80085  if (bytesPerFrame == 0) {
80086  return 0;
80087  }
80088  ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels);
80089  }
80090  return framesRead;
80091 }
80092 MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
80093 {
80094  ma_uint64 framesRead = 0;
80095  if (ma_dr_wav_is_container_be(pWav->container)) {
80096  if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) {
80097  if (ma_dr_wav__is_little_endian()) {
80098  framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
80099  } else {
80100  framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
80101  }
80102  goto post_process;
80103  }
80104  }
80105  if (ma_dr_wav__is_little_endian()) {
80106  framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
80107  } else {
80108  framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
80109  }
80110  post_process:
80111  {
80112  if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) {
80113  if (pBufferOut != NULL) {
80114  ma_uint64 iSample;
80115  for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) {
80116  ((ma_uint8*)pBufferOut)[iSample] += 128;
80117  }
80118  }
80119  }
80120  }
80121  return framesRead;
80122 }
80123 MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav)
80124 {
80125  if (pWav->onWrite != NULL) {
80126  return MA_FALSE;
80127  }
80128  if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) {
80129  return MA_FALSE;
80130  }
80131  if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
80132  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
80133  MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm);
80134  } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
80135  MA_DR_WAV_ZERO_OBJECT(&pWav->ima);
80136  } else {
80137  MA_DR_WAV_ASSERT(MA_FALSE);
80138  }
80139  }
80140  pWav->readCursorInPCMFrames = 0;
80141  pWav->bytesRemaining = pWav->dataChunkDataSize;
80142  return MA_TRUE;
80143 }
80144 MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex)
80145 {
80146  if (pWav == NULL || pWav->onSeek == NULL) {
80147  return MA_FALSE;
80148  }
80149  if (pWav->onWrite != NULL) {
80150  return MA_FALSE;
80151  }
80152  if (pWav->totalPCMFrameCount == 0) {
80153  return MA_TRUE;
80154  }
80155  if (targetFrameIndex > pWav->totalPCMFrameCount) {
80156  targetFrameIndex = pWav->totalPCMFrameCount;
80157  }
80158  if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
80159  if (targetFrameIndex < pWav->readCursorInPCMFrames) {
80160  if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) {
80161  return MA_FALSE;
80162  }
80163  }
80164  if (targetFrameIndex > pWav->readCursorInPCMFrames) {
80165  ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;
80166  ma_int16 devnull[2048];
80167  while (offsetInFrames > 0) {
80168  ma_uint64 framesRead = 0;
80169  ma_uint64 framesToRead = offsetInFrames;
80170  if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) {
80171  framesToRead = ma_dr_wav_countof(devnull)/pWav->channels;
80172  }
80173  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
80174  framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
80175  } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
80176  framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
80177  } else {
80178  MA_DR_WAV_ASSERT(MA_FALSE);
80179  }
80180  if (framesRead != framesToRead) {
80181  return MA_FALSE;
80182  }
80183  offsetInFrames -= framesRead;
80184  }
80185  }
80186  } else {
80187  ma_uint64 totalSizeInBytes;
80188  ma_uint64 currentBytePos;
80189  ma_uint64 targetBytePos;
80190  ma_uint64 offset;
80191  ma_uint32 bytesPerFrame;
80192  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
80193  if (bytesPerFrame == 0) {
80194  return MA_FALSE;
80195  }
80196  totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame;
80197  currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
80198  targetBytePos = targetFrameIndex * bytesPerFrame;
80199  if (currentBytePos < targetBytePos) {
80200  offset = (targetBytePos - currentBytePos);
80201  } else {
80202  if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) {
80203  return MA_FALSE;
80204  }
80205  offset = targetBytePos;
80206  }
80207  while (offset > 0) {
80208  int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
80209  if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) {
80210  return MA_FALSE;
80211  }
80212  pWav->readCursorInPCMFrames += offset32 / bytesPerFrame;
80213  pWav->bytesRemaining -= offset32;
80214  offset -= offset32;
80215  }
80216  }
80217  return MA_TRUE;
80218 }
80219 MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor)
80220 {
80221  if (pCursor == NULL) {
80222  return MA_INVALID_ARGS;
80223  }
80224  *pCursor = 0;
80225  if (pWav == NULL) {
80226  return MA_INVALID_ARGS;
80227  }
80228  *pCursor = pWav->readCursorInPCMFrames;
80229  return MA_SUCCESS;
80230 }
80231 MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength)
80232 {
80233  if (pLength == NULL) {
80234  return MA_INVALID_ARGS;
80235  }
80236  *pLength = 0;
80237  if (pWav == NULL) {
80238  return MA_INVALID_ARGS;
80239  }
80240  *pLength = pWav->totalPCMFrameCount;
80241  return MA_SUCCESS;
80242 }
80243 MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData)
80244 {
80245  size_t bytesWritten;
80246  if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
80247  return 0;
80248  }
80249  bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
80250  pWav->dataChunkDataSize += bytesWritten;
80251  return bytesWritten;
80252 }
80253 MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
80254 {
80255  ma_uint64 bytesToWrite;
80256  ma_uint64 bytesWritten;
80257  const ma_uint8* pRunningData;
80258  if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
80259  return 0;
80260  }
80261  bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
80262  if (bytesToWrite > MA_SIZE_MAX) {
80263  return 0;
80264  }
80265  bytesWritten = 0;
80266  pRunningData = (const ma_uint8*)pData;
80267  while (bytesToWrite > 0) {
80268  size_t bytesJustWritten;
80269  ma_uint64 bytesToWriteThisIteration;
80270  bytesToWriteThisIteration = bytesToWrite;
80271  MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX);
80272  bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
80273  if (bytesJustWritten == 0) {
80274  break;
80275  }
80276  bytesToWrite -= bytesJustWritten;
80277  bytesWritten += bytesJustWritten;
80278  pRunningData += bytesJustWritten;
80279  }
80280  return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
80281 }
80282 MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
80283 {
80284  ma_uint64 bytesToWrite;
80285  ma_uint64 bytesWritten;
80286  ma_uint32 bytesPerSample;
80287  const ma_uint8* pRunningData;
80288  if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
80289  return 0;
80290  }
80291  bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
80292  if (bytesToWrite > MA_SIZE_MAX) {
80293  return 0;
80294  }
80295  bytesWritten = 0;
80296  pRunningData = (const ma_uint8*)pData;
80297  bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
80298  if (bytesPerSample == 0) {
80299  return 0;
80300  }
80301  while (bytesToWrite > 0) {
80302  ma_uint8 temp[4096];
80303  ma_uint32 sampleCount;
80304  size_t bytesJustWritten;
80305  ma_uint64 bytesToWriteThisIteration;
80306  bytesToWriteThisIteration = bytesToWrite;
80307  MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX);
80308  sampleCount = sizeof(temp)/bytesPerSample;
80309  if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) {
80310  bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample;
80311  }
80312  MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
80313  ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample);
80314  bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
80315  if (bytesJustWritten == 0) {
80316  break;
80317  }
80318  bytesToWrite -= bytesJustWritten;
80319  bytesWritten += bytesJustWritten;
80320  pRunningData += bytesJustWritten;
80321  }
80322  return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
80323 }
80324 MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
80325 {
80326  if (ma_dr_wav__is_little_endian()) {
80327  return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData);
80328  } else {
80329  return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData);
80330  }
80331 }
80332 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80333 {
80334  ma_uint64 totalFramesRead = 0;
80335  MA_DR_WAV_ASSERT(pWav != NULL);
80336  MA_DR_WAV_ASSERT(framesToRead > 0);
80337  while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
80338  MA_DR_WAV_ASSERT(framesToRead > 0);
80339  if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
80340  if (pWav->channels == 1) {
80341  ma_uint8 header[7];
80342  if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
80343  return totalFramesRead;
80344  }
80345  pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
80346  pWav->msadpcm.predictor[0] = header[0];
80347  pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1);
80348  pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3);
80349  pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5);
80350  pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
80351  pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
80352  pWav->msadpcm.cachedFrameCount = 2;
80353  } else {
80354  ma_uint8 header[14];
80355  if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
80356  return totalFramesRead;
80357  }
80358  pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
80359  pWav->msadpcm.predictor[0] = header[0];
80360  pWav->msadpcm.predictor[1] = header[1];
80361  pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2);
80362  pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4);
80363  pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6);
80364  pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8);
80365  pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10);
80366  pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12);
80367  pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
80368  pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
80369  pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
80370  pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
80371  pWav->msadpcm.cachedFrameCount = 2;
80372  }
80373  }
80374  while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
80375  if (pBufferOut != NULL) {
80376  ma_uint32 iSample = 0;
80377  for (iSample = 0; iSample < pWav->channels; iSample += 1) {
80378  pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
80379  }
80380  pBufferOut += pWav->channels;
80381  }
80382  framesToRead -= 1;
80383  totalFramesRead += 1;
80384  pWav->readCursorInPCMFrames += 1;
80385  pWav->msadpcm.cachedFrameCount -= 1;
80386  }
80387  if (framesToRead == 0) {
80388  break;
80389  }
80390  if (pWav->msadpcm.cachedFrameCount == 0) {
80391  if (pWav->msadpcm.bytesRemainingInBlock == 0) {
80392  continue;
80393  } else {
80394  static ma_int32 adaptationTable[] = {
80395  230, 230, 230, 230, 307, 409, 512, 614,
80396  768, 614, 512, 409, 307, 230, 230, 230
80397  };
80398  static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
80399  static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
80400  ma_uint8 nibbles;
80401  ma_int32 nibble0;
80402  ma_int32 nibble1;
80403  if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
80404  return totalFramesRead;
80405  }
80406  pWav->msadpcm.bytesRemainingInBlock -= 1;
80407  nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
80408  nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
80409  if (pWav->channels == 1) {
80410  ma_int32 newSample0;
80411  ma_int32 newSample1;
80412  newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
80413  newSample0 += nibble0 * pWav->msadpcm.delta[0];
80414  newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767);
80415  pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
80416  if (pWav->msadpcm.delta[0] < 16) {
80417  pWav->msadpcm.delta[0] = 16;
80418  }
80419  pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
80420  pWav->msadpcm.prevFrames[0][1] = newSample0;
80421  newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
80422  newSample1 += nibble1 * pWav->msadpcm.delta[0];
80423  newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767);
80424  pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
80425  if (pWav->msadpcm.delta[0] < 16) {
80426  pWav->msadpcm.delta[0] = 16;
80427  }
80428  pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
80429  pWav->msadpcm.prevFrames[0][1] = newSample1;
80430  pWav->msadpcm.cachedFrames[2] = newSample0;
80431  pWav->msadpcm.cachedFrames[3] = newSample1;
80432  pWav->msadpcm.cachedFrameCount = 2;
80433  } else {
80434  ma_int32 newSample0;
80435  ma_int32 newSample1;
80436  newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
80437  newSample0 += nibble0 * pWav->msadpcm.delta[0];
80438  newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767);
80439  pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
80440  if (pWav->msadpcm.delta[0] < 16) {
80441  pWav->msadpcm.delta[0] = 16;
80442  }
80443  pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
80444  pWav->msadpcm.prevFrames[0][1] = newSample0;
80445  newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
80446  newSample1 += nibble1 * pWav->msadpcm.delta[1];
80447  newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767);
80448  pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
80449  if (pWav->msadpcm.delta[1] < 16) {
80450  pWav->msadpcm.delta[1] = 16;
80451  }
80452  pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
80453  pWav->msadpcm.prevFrames[1][1] = newSample1;
80454  pWav->msadpcm.cachedFrames[2] = newSample0;
80455  pWav->msadpcm.cachedFrames[3] = newSample1;
80456  pWav->msadpcm.cachedFrameCount = 1;
80457  }
80458  }
80459  }
80460  }
80461  return totalFramesRead;
80462 }
80463 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80464 {
80465  ma_uint64 totalFramesRead = 0;
80466  ma_uint32 iChannel;
80467  static ma_int32 indexTable[16] = {
80468  -1, -1, -1, -1, 2, 4, 6, 8,
80469  -1, -1, -1, -1, 2, 4, 6, 8
80470  };
80471  static ma_int32 stepTable[89] = {
80472  7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
80473  19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
80474  50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
80475  130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
80476  337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
80477  876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
80478  2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
80479  5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
80480  15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
80481  };
80482  MA_DR_WAV_ASSERT(pWav != NULL);
80483  MA_DR_WAV_ASSERT(framesToRead > 0);
80484  while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
80485  MA_DR_WAV_ASSERT(framesToRead > 0);
80486  if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
80487  if (pWav->channels == 1) {
80488  ma_uint8 header[4];
80489  if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
80490  return totalFramesRead;
80491  }
80492  pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
80493  if (header[2] >= ma_dr_wav_countof(stepTable)) {
80494  pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current);
80495  pWav->ima.bytesRemainingInBlock = 0;
80496  return totalFramesRead;
80497  }
80498  pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0);
80499  pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
80500  pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
80501  pWav->ima.cachedFrameCount = 1;
80502  } else {
80503  ma_uint8 header[8];
80504  if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
80505  return totalFramesRead;
80506  }
80507  pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
80508  if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) {
80509  pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current);
80510  pWav->ima.bytesRemainingInBlock = 0;
80511  return totalFramesRead;
80512  }
80513  pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0);
80514  pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
80515  pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4);
80516  pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
80517  pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
80518  pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
80519  pWav->ima.cachedFrameCount = 1;
80520  }
80521  }
80522  while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
80523  if (pBufferOut != NULL) {
80524  ma_uint32 iSample;
80525  for (iSample = 0; iSample < pWav->channels; iSample += 1) {
80526  pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
80527  }
80528  pBufferOut += pWav->channels;
80529  }
80530  framesToRead -= 1;
80531  totalFramesRead += 1;
80532  pWav->readCursorInPCMFrames += 1;
80533  pWav->ima.cachedFrameCount -= 1;
80534  }
80535  if (framesToRead == 0) {
80536  break;
80537  }
80538  if (pWav->ima.cachedFrameCount == 0) {
80539  if (pWav->ima.bytesRemainingInBlock == 0) {
80540  continue;
80541  } else {
80542  pWav->ima.cachedFrameCount = 8;
80543  for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
80544  ma_uint32 iByte;
80545  ma_uint8 nibbles[4];
80546  if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
80547  pWav->ima.cachedFrameCount = 0;
80548  return totalFramesRead;
80549  }
80550  pWav->ima.bytesRemainingInBlock -= 4;
80551  for (iByte = 0; iByte < 4; ++iByte) {
80552  ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
80553  ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
80554  ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
80555  ma_int32 predictor = pWav->ima.predictor[iChannel];
80556  ma_int32 diff = step >> 3;
80557  if (nibble0 & 1) diff += step >> 2;
80558  if (nibble0 & 2) diff += step >> 1;
80559  if (nibble0 & 4) diff += step;
80560  if (nibble0 & 8) diff = -diff;
80561  predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767);
80562  pWav->ima.predictor[iChannel] = predictor;
80563  pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
80564  pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
80565  step = stepTable[pWav->ima.stepIndex[iChannel]];
80566  predictor = pWav->ima.predictor[iChannel];
80567  diff = step >> 3;
80568  if (nibble1 & 1) diff += step >> 2;
80569  if (nibble1 & 2) diff += step >> 1;
80570  if (nibble1 & 4) diff += step;
80571  if (nibble1 & 8) diff = -diff;
80572  predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767);
80573  pWav->ima.predictor[iChannel] = predictor;
80574  pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
80575  pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
80576  }
80577  }
80578  }
80579  }
80580  }
80581  return totalFramesRead;
80582 }
80583 #ifndef MA_DR_WAV_NO_CONVERSION_API
80584 static unsigned short g_ma_dr_wavAlawTable[256] = {
80585  0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
80586  0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
80587  0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
80588  0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
80589  0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
80590  0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
80591  0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
80592  0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
80593  0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
80594  0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
80595  0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
80596  0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
80597  0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
80598  0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
80599  0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
80600  0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
80601 };
80602 static unsigned short g_ma_dr_wavMulawTable[256] = {
80603  0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
80604  0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
80605  0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
80606  0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
80607  0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
80608  0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
80609  0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
80610  0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
80611  0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
80612  0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
80613  0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
80614  0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
80615  0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
80616  0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
80617  0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
80618  0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
80619 };
80620 static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn)
80621 {
80622  return (short)g_ma_dr_wavAlawTable[sampleIn];
80623 }
80624 static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn)
80625 {
80626  return (short)g_ma_dr_wavMulawTable[sampleIn];
80627 }
80628 MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
80629 {
80630  size_t i;
80631  if (bytesPerSample == 1) {
80632  ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount);
80633  return;
80634  }
80635  if (bytesPerSample == 2) {
80636  for (i = 0; i < totalSampleCount; ++i) {
80637  *pOut++ = ((const ma_int16*)pIn)[i];
80638  }
80639  return;
80640  }
80641  if (bytesPerSample == 3) {
80642  ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount);
80643  return;
80644  }
80645  if (bytesPerSample == 4) {
80646  ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount);
80647  return;
80648  }
80649  if (bytesPerSample > 8) {
80650  MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
80651  return;
80652  }
80653  for (i = 0; i < totalSampleCount; ++i) {
80654  ma_uint64 sample = 0;
80655  unsigned int shift = (8 - bytesPerSample) * 8;
80656  unsigned int j;
80657  for (j = 0; j < bytesPerSample; j += 1) {
80658  MA_DR_WAV_ASSERT(j < 8);
80659  sample |= (ma_uint64)(pIn[j]) << shift;
80660  shift += 8;
80661  }
80662  pIn += j;
80663  *pOut++ = (ma_int16)((ma_int64)sample >> 48);
80664  }
80665 }
80666 MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
80667 {
80668  if (bytesPerSample == 4) {
80669  ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
80670  return;
80671  } else if (bytesPerSample == 8) {
80672  ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
80673  return;
80674  } else {
80675  MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
80676  return;
80677  }
80678 }
80679 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80680 {
80681  ma_uint64 totalFramesRead;
80682  ma_uint8 sampleData[4096] = {0};
80683  ma_uint32 bytesPerFrame;
80684  ma_uint32 bytesPerSample;
80685  ma_uint64 samplesRead;
80686  if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
80687  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
80688  }
80689  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
80690  if (bytesPerFrame == 0) {
80691  return 0;
80692  }
80693  bytesPerSample = bytesPerFrame / pWav->channels;
80694  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80695  return 0;
80696  }
80697  totalFramesRead = 0;
80698  while (framesToRead > 0) {
80699  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80700  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80701  if (framesRead == 0) {
80702  break;
80703  }
80704  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
80705  samplesRead = framesRead * pWav->channels;
80706  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80707  MA_DR_WAV_ASSERT(MA_FALSE);
80708  break;
80709  }
80710  ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
80711  pBufferOut += samplesRead;
80712  framesToRead -= framesRead;
80713  totalFramesRead += framesRead;
80714  }
80715  return totalFramesRead;
80716 }
80717 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80718 {
80719  ma_uint64 totalFramesRead;
80720  ma_uint8 sampleData[4096] = {0};
80721  ma_uint32 bytesPerFrame;
80722  ma_uint32 bytesPerSample;
80723  ma_uint64 samplesRead;
80724  if (pBufferOut == NULL) {
80725  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
80726  }
80727  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
80728  if (bytesPerFrame == 0) {
80729  return 0;
80730  }
80731  bytesPerSample = bytesPerFrame / pWav->channels;
80732  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80733  return 0;
80734  }
80735  totalFramesRead = 0;
80736  while (framesToRead > 0) {
80737  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80738  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80739  if (framesRead == 0) {
80740  break;
80741  }
80742  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
80743  samplesRead = framesRead * pWav->channels;
80744  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80745  MA_DR_WAV_ASSERT(MA_FALSE);
80746  break;
80747  }
80748  ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
80749  pBufferOut += samplesRead;
80750  framesToRead -= framesRead;
80751  totalFramesRead += framesRead;
80752  }
80753  return totalFramesRead;
80754 }
80755 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80756 {
80757  ma_uint64 totalFramesRead;
80758  ma_uint8 sampleData[4096] = {0};
80759  ma_uint32 bytesPerFrame;
80760  ma_uint32 bytesPerSample;
80761  ma_uint64 samplesRead;
80762  if (pBufferOut == NULL) {
80763  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
80764  }
80765  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
80766  if (bytesPerFrame == 0) {
80767  return 0;
80768  }
80769  bytesPerSample = bytesPerFrame / pWav->channels;
80770  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80771  return 0;
80772  }
80773  totalFramesRead = 0;
80774  while (framesToRead > 0) {
80775  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80776  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80777  if (framesRead == 0) {
80778  break;
80779  }
80780  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
80781  samplesRead = framesRead * pWav->channels;
80782  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80783  MA_DR_WAV_ASSERT(MA_FALSE);
80784  break;
80785  }
80786  ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
80787  #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
80788  {
80789  if (pWav->container == ma_dr_wav_container_aiff) {
80790  ma_uint64 iSample;
80791  for (iSample = 0; iSample < samplesRead; iSample += 1) {
80792  pBufferOut[iSample] = -pBufferOut[iSample];
80793  }
80794  }
80795  }
80796  #endif
80797  pBufferOut += samplesRead;
80798  framesToRead -= framesRead;
80799  totalFramesRead += framesRead;
80800  }
80801  return totalFramesRead;
80802 }
80803 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80804 {
80805  ma_uint64 totalFramesRead;
80806  ma_uint8 sampleData[4096] = {0};
80807  ma_uint32 bytesPerFrame;
80808  ma_uint32 bytesPerSample;
80809  ma_uint64 samplesRead;
80810  if (pBufferOut == NULL) {
80811  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
80812  }
80813  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
80814  if (bytesPerFrame == 0) {
80815  return 0;
80816  }
80817  bytesPerSample = bytesPerFrame / pWav->channels;
80818  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80819  return 0;
80820  }
80821  totalFramesRead = 0;
80822  while (framesToRead > 0) {
80823  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80824  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80825  if (framesRead == 0) {
80826  break;
80827  }
80828  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
80829  samplesRead = framesRead * pWav->channels;
80830  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80831  MA_DR_WAV_ASSERT(MA_FALSE);
80832  break;
80833  }
80834  ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
80835  #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
80836  {
80837  if (pWav->container == ma_dr_wav_container_aiff) {
80838  ma_uint64 iSample;
80839  for (iSample = 0; iSample < samplesRead; iSample += 1) {
80840  pBufferOut[iSample] = -pBufferOut[iSample];
80841  }
80842  }
80843  }
80844  #endif
80845  pBufferOut += samplesRead;
80846  framesToRead -= framesRead;
80847  totalFramesRead += framesRead;
80848  }
80849  return totalFramesRead;
80850 }
80851 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80852 {
80853  if (pWav == NULL || framesToRead == 0) {
80854  return 0;
80855  }
80856  if (pBufferOut == NULL) {
80857  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
80858  }
80859  if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) {
80860  framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels;
80861  }
80862  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
80863  return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
80864  }
80865  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
80866  return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
80867  }
80868  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
80869  return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
80870  }
80871  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
80872  return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
80873  }
80874  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
80875  return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
80876  }
80877  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
80878  return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
80879  }
80880  return 0;
80881 }
80882 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80883 {
80884  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
80885  if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
80886  ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
80887  }
80888  return framesRead;
80889 }
80890 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
80891 {
80892  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
80893  if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
80894  ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
80895  }
80896  return framesRead;
80897 }
80898 MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
80899 {
80900  int r;
80901  size_t i;
80902  for (i = 0; i < sampleCount; ++i) {
80903  int x = pIn[i];
80904  r = x << 8;
80905  r = r - 32768;
80906  pOut[i] = (short)r;
80907  }
80908 }
80909 MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
80910 {
80911  int r;
80912  size_t i;
80913  for (i = 0; i < sampleCount; ++i) {
80914  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;
80915  r = x >> 8;
80916  pOut[i] = (short)r;
80917  }
80918 }
80919 MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount)
80920 {
80921  int r;
80922  size_t i;
80923  for (i = 0; i < sampleCount; ++i) {
80924  int x = pIn[i];
80925  r = x >> 16;
80926  pOut[i] = (short)r;
80927  }
80928 }
80929 MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount)
80930 {
80931  int r;
80932  size_t i;
80933  for (i = 0; i < sampleCount; ++i) {
80934  float x = pIn[i];
80935  float c;
80936  c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
80937  c = c + 1;
80938  r = (int)(c * 32767.5f);
80939  r = r - 32768;
80940  pOut[i] = (short)r;
80941  }
80942 }
80943 MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount)
80944 {
80945  int r;
80946  size_t i;
80947  for (i = 0; i < sampleCount; ++i) {
80948  double x = pIn[i];
80949  double c;
80950  c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
80951  c = c + 1;
80952  r = (int)(c * 32767.5);
80953  r = r - 32768;
80954  pOut[i] = (short)r;
80955  }
80956 }
80957 MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
80958 {
80959  size_t i;
80960  for (i = 0; i < sampleCount; ++i) {
80961  pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]);
80962  }
80963 }
80964 MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
80965 {
80966  size_t i;
80967  for (i = 0; i < sampleCount; ++i) {
80968  pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]);
80969  }
80970 }
80971 MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
80972 {
80973  unsigned int i;
80974  if (bytesPerSample == 1) {
80975  ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount);
80976  return;
80977  }
80978  if (bytesPerSample == 2) {
80979  ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount);
80980  return;
80981  }
80982  if (bytesPerSample == 3) {
80983  ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount);
80984  return;
80985  }
80986  if (bytesPerSample == 4) {
80987  ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount);
80988  return;
80989  }
80990  if (bytesPerSample > 8) {
80991  MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
80992  return;
80993  }
80994  for (i = 0; i < sampleCount; ++i) {
80995  ma_uint64 sample = 0;
80996  unsigned int shift = (8 - bytesPerSample) * 8;
80997  unsigned int j;
80998  for (j = 0; j < bytesPerSample; j += 1) {
80999  MA_DR_WAV_ASSERT(j < 8);
81000  sample |= (ma_uint64)(pIn[j]) << shift;
81001  shift += 8;
81002  }
81003  pIn += j;
81004  *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0);
81005  }
81006 }
81007 MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
81008 {
81009  if (bytesPerSample == 4) {
81010  unsigned int i;
81011  for (i = 0; i < sampleCount; ++i) {
81012  *pOut++ = ((const float*)pIn)[i];
81013  }
81014  return;
81015  } else if (bytesPerSample == 8) {
81016  ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
81017  return;
81018  } else {
81019  MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
81020  return;
81021  }
81022 }
81023 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
81024 {
81025  ma_uint64 totalFramesRead;
81026  ma_uint8 sampleData[4096] = {0};
81027  ma_uint32 bytesPerFrame;
81028  ma_uint32 bytesPerSample;
81029  ma_uint64 samplesRead;
81030  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81031  if (bytesPerFrame == 0) {
81032  return 0;
81033  }
81034  bytesPerSample = bytesPerFrame / pWav->channels;
81035  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81036  return 0;
81037  }
81038  totalFramesRead = 0;
81039  while (framesToRead > 0) {
81040  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81041  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81042  if (framesRead == 0) {
81043  break;
81044  }
81045  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81046  samplesRead = framesRead * pWav->channels;
81047  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81048  MA_DR_WAV_ASSERT(MA_FALSE);
81049  break;
81050  }
81051  ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
81052  pBufferOut += samplesRead;
81053  framesToRead -= framesRead;
81054  totalFramesRead += framesRead;
81055  }
81056  return totalFramesRead;
81057 }
81058 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
81059 {
81060  ma_uint64 totalFramesRead;
81061  ma_int16 samples16[2048];
81062  totalFramesRead = 0;
81063  while (framesToRead > 0) {
81064  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels);
81065  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
81066  if (framesRead == 0) {
81067  break;
81068  }
81069  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81070  ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
81071  pBufferOut += framesRead*pWav->channels;
81072  framesToRead -= framesRead;
81073  totalFramesRead += framesRead;
81074  }
81075  return totalFramesRead;
81076 }
81077 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
81078 {
81079  ma_uint64 totalFramesRead;
81080  ma_uint8 sampleData[4096] = {0};
81081  ma_uint32 bytesPerFrame;
81082  ma_uint32 bytesPerSample;
81083  ma_uint64 samplesRead;
81084  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
81085  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
81086  }
81087  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81088  if (bytesPerFrame == 0) {
81089  return 0;
81090  }
81091  bytesPerSample = bytesPerFrame / pWav->channels;
81092  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81093  return 0;
81094  }
81095  totalFramesRead = 0;
81096  while (framesToRead > 0) {
81097  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81098  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81099  if (framesRead == 0) {
81100  break;
81101  }
81102  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81103  samplesRead = framesRead * pWav->channels;
81104  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81105  MA_DR_WAV_ASSERT(MA_FALSE);
81106  break;
81107  }
81108  ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
81109  pBufferOut += samplesRead;
81110  framesToRead -= framesRead;
81111  totalFramesRead += framesRead;
81112  }
81113  return totalFramesRead;
81114 }
81115 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
81116 {
81117  ma_uint64 totalFramesRead;
81118  ma_uint8 sampleData[4096] = {0};
81119  ma_uint32 bytesPerFrame;
81120  ma_uint32 bytesPerSample;
81121  ma_uint64 samplesRead;
81122  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81123  if (bytesPerFrame == 0) {
81124  return 0;
81125  }
81126  bytesPerSample = bytesPerFrame / pWav->channels;
81127  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81128  return 0;
81129  }
81130  totalFramesRead = 0;
81131  while (framesToRead > 0) {
81132  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81133  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81134  if (framesRead == 0) {
81135  break;
81136  }
81137  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81138  samplesRead = framesRead * pWav->channels;
81139  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81140  MA_DR_WAV_ASSERT(MA_FALSE);
81141  break;
81142  }
81143  ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
81144  #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
81145  {
81146  if (pWav->container == ma_dr_wav_container_aiff) {
81147  ma_uint64 iSample;
81148  for (iSample = 0; iSample < samplesRead; iSample += 1) {
81149  pBufferOut[iSample] = -pBufferOut[iSample];
81150  }
81151  }
81152  }
81153  #endif
81154  pBufferOut += samplesRead;
81155  framesToRead -= framesRead;
81156  totalFramesRead += framesRead;
81157  }
81158  return totalFramesRead;
81159 }
81160 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
81161 {
81162  ma_uint64 totalFramesRead;
81163  ma_uint8 sampleData[4096] = {0};
81164  ma_uint32 bytesPerFrame;
81165  ma_uint32 bytesPerSample;
81166  ma_uint64 samplesRead;
81167  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81168  if (bytesPerFrame == 0) {
81169  return 0;
81170  }
81171  bytesPerSample = bytesPerFrame / pWav->channels;
81172  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81173  return 0;
81174  }
81175  totalFramesRead = 0;
81176  while (framesToRead > 0) {
81177  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81178  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81179  if (framesRead == 0) {
81180  break;
81181  }
81182  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81183  samplesRead = framesRead * pWav->channels;
81184  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81185  MA_DR_WAV_ASSERT(MA_FALSE);
81186  break;
81187  }
81188  ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
81189  #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
81190  {
81191  if (pWav->container == ma_dr_wav_container_aiff) {
81192  ma_uint64 iSample;
81193  for (iSample = 0; iSample < samplesRead; iSample += 1) {
81194  pBufferOut[iSample] = -pBufferOut[iSample];
81195  }
81196  }
81197  }
81198  #endif
81199  pBufferOut += samplesRead;
81200  framesToRead -= framesRead;
81201  totalFramesRead += framesRead;
81202  }
81203  return totalFramesRead;
81204 }
81205 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
81206 {
81207  if (pWav == NULL || framesToRead == 0) {
81208  return 0;
81209  }
81210  if (pBufferOut == NULL) {
81211  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
81212  }
81213  if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) {
81214  framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels;
81215  }
81216  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
81217  return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
81218  }
81219  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
81220  return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut);
81221  }
81222  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
81223  return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
81224  }
81225  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
81226  return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
81227  }
81228  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
81229  return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
81230  }
81231  return 0;
81232 }
81233 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
81234 {
81235  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
81236  if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
81237  ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
81238  }
81239  return framesRead;
81240 }
81241 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
81242 {
81243  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
81244  if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
81245  ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
81246  }
81247  return framesRead;
81248 }
81249 MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
81250 {
81251  size_t i;
81252  if (pOut == NULL || pIn == NULL) {
81253  return;
81254  }
81255 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
81256  for (i = 0; i < sampleCount; ++i) {
81257  *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
81258  }
81259 #else
81260  for (i = 0; i < sampleCount; ++i) {
81261  float x = pIn[i];
81262  x = x * 0.00784313725490196078f;
81263  x = x - 1;
81264  *pOut++ = x;
81265  }
81266 #endif
81267 }
81268 MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount)
81269 {
81270  size_t i;
81271  if (pOut == NULL || pIn == NULL) {
81272  return;
81273  }
81274  for (i = 0; i < sampleCount; ++i) {
81275  *pOut++ = pIn[i] * 0.000030517578125f;
81276  }
81277 }
81278 MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
81279 {
81280  size_t i;
81281  if (pOut == NULL || pIn == NULL) {
81282  return;
81283  }
81284  for (i = 0; i < sampleCount; ++i) {
81285  double x;
81286  ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8);
81287  ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16);
81288  ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24);
81289  x = (double)((ma_int32)(a | b | c) >> 8);
81290  *pOut++ = (float)(x * 0.00000011920928955078125);
81291  }
81292 }
81293 MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount)
81294 {
81295  size_t i;
81296  if (pOut == NULL || pIn == NULL) {
81297  return;
81298  }
81299  for (i = 0; i < sampleCount; ++i) {
81300  *pOut++ = (float)(pIn[i] / 2147483648.0);
81301  }
81302 }
81303 MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
81304 {
81305  size_t i;
81306  if (pOut == NULL || pIn == NULL) {
81307  return;
81308  }
81309  for (i = 0; i < sampleCount; ++i) {
81310  *pOut++ = (float)pIn[i];
81311  }
81312 }
81313 MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
81314 {
81315  size_t i;
81316  if (pOut == NULL || pIn == NULL) {
81317  return;
81318  }
81319  for (i = 0; i < sampleCount; ++i) {
81320  *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f;
81321  }
81322 }
81323 MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
81324 {
81325  size_t i;
81326  if (pOut == NULL || pIn == NULL) {
81327  return;
81328  }
81329  for (i = 0; i < sampleCount; ++i) {
81330  *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f;
81331  }
81332 }
81333 MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
81334 {
81335  unsigned int i;
81336  if (bytesPerSample == 1) {
81337  ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount);
81338  return;
81339  }
81340  if (bytesPerSample == 2) {
81341  ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount);
81342  return;
81343  }
81344  if (bytesPerSample == 3) {
81345  ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount);
81346  return;
81347  }
81348  if (bytesPerSample == 4) {
81349  for (i = 0; i < totalSampleCount; ++i) {
81350  *pOut++ = ((const ma_int32*)pIn)[i];
81351  }
81352  return;
81353  }
81354  if (bytesPerSample > 8) {
81355  MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
81356  return;
81357  }
81358  for (i = 0; i < totalSampleCount; ++i) {
81359  ma_uint64 sample = 0;
81360  unsigned int shift = (8 - bytesPerSample) * 8;
81361  unsigned int j;
81362  for (j = 0; j < bytesPerSample; j += 1) {
81363  MA_DR_WAV_ASSERT(j < 8);
81364  sample |= (ma_uint64)(pIn[j]) << shift;
81365  shift += 8;
81366  }
81367  pIn += j;
81368  *pOut++ = (ma_int32)((ma_int64)sample >> 32);
81369  }
81370 }
81371 MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
81372 {
81373  if (bytesPerSample == 4) {
81374  ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
81375  return;
81376  } else if (bytesPerSample == 8) {
81377  ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
81378  return;
81379  } else {
81380  MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
81381  return;
81382  }
81383 }
81384 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
81385 {
81386  ma_uint64 totalFramesRead;
81387  ma_uint8 sampleData[4096] = {0};
81388  ma_uint32 bytesPerFrame;
81389  ma_uint32 bytesPerSample;
81390  ma_uint64 samplesRead;
81391  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
81392  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
81393  }
81394  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81395  if (bytesPerFrame == 0) {
81396  return 0;
81397  }
81398  bytesPerSample = bytesPerFrame / pWav->channels;
81399  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81400  return 0;
81401  }
81402  totalFramesRead = 0;
81403  while (framesToRead > 0) {
81404  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81405  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81406  if (framesRead == 0) {
81407  break;
81408  }
81409  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81410  samplesRead = framesRead * pWav->channels;
81411  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81412  MA_DR_WAV_ASSERT(MA_FALSE);
81413  break;
81414  }
81415  ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
81416  pBufferOut += samplesRead;
81417  framesToRead -= framesRead;
81418  totalFramesRead += framesRead;
81419  }
81420  return totalFramesRead;
81421 }
81422 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
81423 {
81424  ma_uint64 totalFramesRead = 0;
81425  ma_int16 samples16[2048];
81426  while (framesToRead > 0) {
81427  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels);
81428  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
81429  if (framesRead == 0) {
81430  break;
81431  }
81432  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81433  ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
81434  pBufferOut += framesRead*pWav->channels;
81435  framesToRead -= framesRead;
81436  totalFramesRead += framesRead;
81437  }
81438  return totalFramesRead;
81439 }
81440 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
81441 {
81442  ma_uint64 totalFramesRead;
81443  ma_uint8 sampleData[4096] = {0};
81444  ma_uint32 bytesPerFrame;
81445  ma_uint32 bytesPerSample;
81446  ma_uint64 samplesRead;
81447  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81448  if (bytesPerFrame == 0) {
81449  return 0;
81450  }
81451  bytesPerSample = bytesPerFrame / pWav->channels;
81452  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81453  return 0;
81454  }
81455  totalFramesRead = 0;
81456  while (framesToRead > 0) {
81457  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81458  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81459  if (framesRead == 0) {
81460  break;
81461  }
81462  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81463  samplesRead = framesRead * pWav->channels;
81464  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81465  MA_DR_WAV_ASSERT(MA_FALSE);
81466  break;
81467  }
81468  ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
81469  pBufferOut += samplesRead;
81470  framesToRead -= framesRead;
81471  totalFramesRead += framesRead;
81472  }
81473  return totalFramesRead;
81474 }
81475 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
81476 {
81477  ma_uint64 totalFramesRead;
81478  ma_uint8 sampleData[4096] = {0};
81479  ma_uint32 bytesPerFrame;
81480  ma_uint32 bytesPerSample;
81481  ma_uint64 samplesRead;
81482  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81483  if (bytesPerFrame == 0) {
81484  return 0;
81485  }
81486  bytesPerSample = bytesPerFrame / pWav->channels;
81487  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81488  return 0;
81489  }
81490  totalFramesRead = 0;
81491  while (framesToRead > 0) {
81492  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81493  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81494  if (framesRead == 0) {
81495  break;
81496  }
81497  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81498  samplesRead = framesRead * pWav->channels;
81499  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81500  MA_DR_WAV_ASSERT(MA_FALSE);
81501  break;
81502  }
81503  ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
81504  #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
81505  {
81506  if (pWav->container == ma_dr_wav_container_aiff) {
81507  ma_uint64 iSample;
81508  for (iSample = 0; iSample < samplesRead; iSample += 1) {
81509  pBufferOut[iSample] = -pBufferOut[iSample];
81510  }
81511  }
81512  }
81513  #endif
81514  pBufferOut += samplesRead;
81515  framesToRead -= framesRead;
81516  totalFramesRead += framesRead;
81517  }
81518  return totalFramesRead;
81519 }
81520 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
81521 {
81522  ma_uint64 totalFramesRead;
81523  ma_uint8 sampleData[4096] = {0};
81524  ma_uint32 bytesPerFrame;
81525  ma_uint32 bytesPerSample;
81526  ma_uint64 samplesRead;
81527  bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81528  if (bytesPerFrame == 0) {
81529  return 0;
81530  }
81531  bytesPerSample = bytesPerFrame / pWav->channels;
81532  if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81533  return 0;
81534  }
81535  totalFramesRead = 0;
81536  while (framesToRead > 0) {
81537  ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81538  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81539  if (framesRead == 0) {
81540  break;
81541  }
81542  MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
81543  samplesRead = framesRead * pWav->channels;
81544  if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81545  MA_DR_WAV_ASSERT(MA_FALSE);
81546  break;
81547  }
81548  ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
81549  #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
81550  {
81551  if (pWav->container == ma_dr_wav_container_aiff) {
81552  ma_uint64 iSample;
81553  for (iSample = 0; iSample < samplesRead; iSample += 1) {
81554  pBufferOut[iSample] = -pBufferOut[iSample];
81555  }
81556  }
81557  }
81558  #endif
81559  pBufferOut += samplesRead;
81560  framesToRead -= framesRead;
81561  totalFramesRead += framesRead;
81562  }
81563  return totalFramesRead;
81564 }
81565 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
81566 {
81567  if (pWav == NULL || framesToRead == 0) {
81568  return 0;
81569  }
81570  if (pBufferOut == NULL) {
81571  return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
81572  }
81573  if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) {
81574  framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels;
81575  }
81576  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
81577  return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
81578  }
81579  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
81580  return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut);
81581  }
81582  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
81583  return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
81584  }
81585  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
81586  return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
81587  }
81588  if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
81589  return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
81590  }
81591  return 0;
81592 }
81593 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
81594 {
81595  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
81596  if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
81597  ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
81598  }
81599  return framesRead;
81600 }
81601 MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
81602 {
81603  ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
81604  if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
81605  ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
81606  }
81607  return framesRead;
81608 }
81609 MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
81610 {
81611  size_t i;
81612  if (pOut == NULL || pIn == NULL) {
81613  return;
81614  }
81615  for (i = 0; i < sampleCount; ++i) {
81616  *pOut++ = ((int)pIn[i] - 128) << 24;
81617  }
81618 }
81619 MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount)
81620 {
81621  size_t i;
81622  if (pOut == NULL || pIn == NULL) {
81623  return;
81624  }
81625  for (i = 0; i < sampleCount; ++i) {
81626  *pOut++ = pIn[i] << 16;
81627  }
81628 }
81629 MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
81630 {
81631  size_t i;
81632  if (pOut == NULL || pIn == NULL) {
81633  return;
81634  }
81635  for (i = 0; i < sampleCount; ++i) {
81636  unsigned int s0 = pIn[i*3 + 0];
81637  unsigned int s1 = pIn[i*3 + 1];
81638  unsigned int s2 = pIn[i*3 + 2];
81639  ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
81640  *pOut++ = sample32;
81641  }
81642 }
81643 MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount)
81644 {
81645  size_t i;
81646  if (pOut == NULL || pIn == NULL) {
81647  return;
81648  }
81649  for (i = 0; i < sampleCount; ++i) {
81650  *pOut++ = (ma_int32)(2147483648.0 * pIn[i]);
81651  }
81652 }
81653 MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount)
81654 {
81655  size_t i;
81656  if (pOut == NULL || pIn == NULL) {
81657  return;
81658  }
81659  for (i = 0; i < sampleCount; ++i) {
81660  *pOut++ = (ma_int32)(2147483648.0 * pIn[i]);
81661  }
81662 }
81663 MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
81664 {
81665  size_t i;
81666  if (pOut == NULL || pIn == NULL) {
81667  return;
81668  }
81669  for (i = 0; i < sampleCount; ++i) {
81670  *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16;
81671  }
81672 }
81673 MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
81674 {
81675  size_t i;
81676  if (pOut == NULL || pIn == NULL) {
81677  return;
81678  }
81679  for (i= 0; i < sampleCount; ++i) {
81680  *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16;
81681  }
81682 }
81683 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)
81684 {
81685  ma_uint64 sampleDataSize;
81686  ma_int16* pSampleData;
81687  ma_uint64 framesRead;
81688  MA_DR_WAV_ASSERT(pWav != NULL);
81689  sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16);
81690  if (sampleDataSize > MA_SIZE_MAX) {
81691  ma_dr_wav_uninit(pWav);
81692  return NULL;
81693  }
81694  pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
81695  if (pSampleData == NULL) {
81696  ma_dr_wav_uninit(pWav);
81697  return NULL;
81698  }
81699  framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
81700  if (framesRead != pWav->totalPCMFrameCount) {
81701  ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
81702  ma_dr_wav_uninit(pWav);
81703  return NULL;
81704  }
81705  ma_dr_wav_uninit(pWav);
81706  if (sampleRate) {
81707  *sampleRate = pWav->sampleRate;
81708  }
81709  if (channels) {
81710  *channels = pWav->channels;
81711  }
81712  if (totalFrameCount) {
81713  *totalFrameCount = pWav->totalPCMFrameCount;
81714  }
81715  return pSampleData;
81716 }
81717 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)
81718 {
81719  ma_uint64 sampleDataSize;
81720  float* pSampleData;
81721  ma_uint64 framesRead;
81722  MA_DR_WAV_ASSERT(pWav != NULL);
81723  sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
81724  if (sampleDataSize > MA_SIZE_MAX) {
81725  ma_dr_wav_uninit(pWav);
81726  return NULL;
81727  }
81728  pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
81729  if (pSampleData == NULL) {
81730  ma_dr_wav_uninit(pWav);
81731  return NULL;
81732  }
81733  framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
81734  if (framesRead != pWav->totalPCMFrameCount) {
81735  ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
81736  ma_dr_wav_uninit(pWav);
81737  return NULL;
81738  }
81739  ma_dr_wav_uninit(pWav);
81740  if (sampleRate) {
81741  *sampleRate = pWav->sampleRate;
81742  }
81743  if (channels) {
81744  *channels = pWav->channels;
81745  }
81746  if (totalFrameCount) {
81747  *totalFrameCount = pWav->totalPCMFrameCount;
81748  }
81749  return pSampleData;
81750 }
81751 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)
81752 {
81753  ma_uint64 sampleDataSize;
81754  ma_int32* pSampleData;
81755  ma_uint64 framesRead;
81756  MA_DR_WAV_ASSERT(pWav != NULL);
81757  sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32);
81758  if (sampleDataSize > MA_SIZE_MAX) {
81759  ma_dr_wav_uninit(pWav);
81760  return NULL;
81761  }
81762  pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
81763  if (pSampleData == NULL) {
81764  ma_dr_wav_uninit(pWav);
81765  return NULL;
81766  }
81767  framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
81768  if (framesRead != pWav->totalPCMFrameCount) {
81769  ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
81770  ma_dr_wav_uninit(pWav);
81771  return NULL;
81772  }
81773  ma_dr_wav_uninit(pWav);
81774  if (sampleRate) {
81775  *sampleRate = pWav->sampleRate;
81776  }
81777  if (channels) {
81778  *channels = pWav->channels;
81779  }
81780  if (totalFrameCount) {
81781  *totalFrameCount = pWav->totalPCMFrameCount;
81782  }
81783  return pSampleData;
81784 }
81785 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)
81786 {
81787  ma_dr_wav wav;
81788  if (channelsOut) {
81789  *channelsOut = 0;
81790  }
81791  if (sampleRateOut) {
81792  *sampleRateOut = 0;
81793  }
81794  if (totalFrameCountOut) {
81795  *totalFrameCountOut = 0;
81796  }
81797  if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
81798  return NULL;
81799  }
81800  return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81801 }
81802 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)
81803 {
81804  ma_dr_wav wav;
81805  if (channelsOut) {
81806  *channelsOut = 0;
81807  }
81808  if (sampleRateOut) {
81809  *sampleRateOut = 0;
81810  }
81811  if (totalFrameCountOut) {
81812  *totalFrameCountOut = 0;
81813  }
81814  if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
81815  return NULL;
81816  }
81817  return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81818 }
81819 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)
81820 {
81821  ma_dr_wav wav;
81822  if (channelsOut) {
81823  *channelsOut = 0;
81824  }
81825  if (sampleRateOut) {
81826  *sampleRateOut = 0;
81827  }
81828  if (totalFrameCountOut) {
81829  *totalFrameCountOut = 0;
81830  }
81831  if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
81832  return NULL;
81833  }
81834  return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81835 }
81836 #ifndef MA_DR_WAV_NO_STDIO
81837 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)
81838 {
81839  ma_dr_wav wav;
81840  if (channelsOut) {
81841  *channelsOut = 0;
81842  }
81843  if (sampleRateOut) {
81844  *sampleRateOut = 0;
81845  }
81846  if (totalFrameCountOut) {
81847  *totalFrameCountOut = 0;
81848  }
81849  if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
81850  return NULL;
81851  }
81852  return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81853 }
81854 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)
81855 {
81856  ma_dr_wav wav;
81857  if (channelsOut) {
81858  *channelsOut = 0;
81859  }
81860  if (sampleRateOut) {
81861  *sampleRateOut = 0;
81862  }
81863  if (totalFrameCountOut) {
81864  *totalFrameCountOut = 0;
81865  }
81866  if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
81867  return NULL;
81868  }
81869  return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81870 }
81871 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)
81872 {
81873  ma_dr_wav wav;
81874  if (channelsOut) {
81875  *channelsOut = 0;
81876  }
81877  if (sampleRateOut) {
81878  *sampleRateOut = 0;
81879  }
81880  if (totalFrameCountOut) {
81881  *totalFrameCountOut = 0;
81882  }
81883  if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
81884  return NULL;
81885  }
81886  return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81887 }
81888 #ifndef MA_DR_WAV_NO_WCHAR
81889 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)
81890 {
81891  ma_dr_wav wav;
81892  if (sampleRateOut) {
81893  *sampleRateOut = 0;
81894  }
81895  if (channelsOut) {
81896  *channelsOut = 0;
81897  }
81898  if (totalFrameCountOut) {
81899  *totalFrameCountOut = 0;
81900  }
81901  if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
81902  return NULL;
81903  }
81904  return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81905 }
81906 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)
81907 {
81908  ma_dr_wav wav;
81909  if (sampleRateOut) {
81910  *sampleRateOut = 0;
81911  }
81912  if (channelsOut) {
81913  *channelsOut = 0;
81914  }
81915  if (totalFrameCountOut) {
81916  *totalFrameCountOut = 0;
81917  }
81918  if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
81919  return NULL;
81920  }
81921  return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81922 }
81923 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)
81924 {
81925  ma_dr_wav wav;
81926  if (sampleRateOut) {
81927  *sampleRateOut = 0;
81928  }
81929  if (channelsOut) {
81930  *channelsOut = 0;
81931  }
81932  if (totalFrameCountOut) {
81933  *totalFrameCountOut = 0;
81934  }
81935  if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
81936  return NULL;
81937  }
81938  return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81939 }
81940 #endif
81941 #endif
81942 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)
81943 {
81944  ma_dr_wav wav;
81945  if (channelsOut) {
81946  *channelsOut = 0;
81947  }
81948  if (sampleRateOut) {
81949  *sampleRateOut = 0;
81950  }
81951  if (totalFrameCountOut) {
81952  *totalFrameCountOut = 0;
81953  }
81954  if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
81955  return NULL;
81956  }
81957  return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81958 }
81959 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)
81960 {
81961  ma_dr_wav wav;
81962  if (channelsOut) {
81963  *channelsOut = 0;
81964  }
81965  if (sampleRateOut) {
81966  *sampleRateOut = 0;
81967  }
81968  if (totalFrameCountOut) {
81969  *totalFrameCountOut = 0;
81970  }
81971  if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
81972  return NULL;
81973  }
81974  return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81975 }
81976 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)
81977 {
81978  ma_dr_wav wav;
81979  if (channelsOut) {
81980  *channelsOut = 0;
81981  }
81982  if (sampleRateOut) {
81983  *sampleRateOut = 0;
81984  }
81985  if (totalFrameCountOut) {
81986  *totalFrameCountOut = 0;
81987  }
81988  if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
81989  return NULL;
81990  }
81991  return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81992 }
81993 #endif
81994 MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
81995 {
81996  if (pAllocationCallbacks != NULL) {
81997  ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks);
81998  } else {
81999  ma_dr_wav__free_default(p, NULL);
82000  }
82001 }
82002 MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data)
82003 {
82004  return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8);
82005 }
82006 MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data)
82007 {
82008  return (ma_int16)ma_dr_wav_bytes_to_u16(data);
82009 }
82010 MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data)
82011 {
82012  return ma_dr_wav_bytes_to_u32_le(data);
82013 }
82014 MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data)
82015 {
82016  union {
82017  ma_uint32 u32;
82018  float f32;
82019  } value;
82020  value.u32 = ma_dr_wav_bytes_to_u32(data);
82021  return value.f32;
82022 }
82023 MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data)
82024 {
82025  return (ma_int32)ma_dr_wav_bytes_to_u32(data);
82026 }
82027 MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data)
82028 {
82029  return
82030  ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) |
82031  ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56);
82032 }
82033 MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data)
82034 {
82035  return (ma_int64)ma_dr_wav_bytes_to_u64(data);
82036 }
82037 MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16])
82038 {
82039  int i;
82040  for (i = 0; i < 16; i += 1) {
82041  if (a[i] != b[i]) {
82042  return MA_FALSE;
82043  }
82044  }
82045  return MA_TRUE;
82046 }
82047 MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b)
82048 {
82049  return
82050  a[0] == b[0] &&
82051  a[1] == b[1] &&
82052  a[2] == b[2] &&
82053  a[3] == b[3];
82054 }
82055 #ifdef __MRC__
82056 #pragma options opt reset
82057 #endif
82058 #endif
82059 /* dr_wav_c end */
82060 #endif /* MA_DR_WAV_IMPLEMENTATION */
82061 #endif /* MA_NO_WAV */
82062 
82063 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
82064 #if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
82065 /* dr_flac_c begin */
82066 #ifndef ma_dr_flac_c
82067 #define ma_dr_flac_c
82068 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
82069  #pragma GCC diagnostic push
82070  #if __GNUC__ >= 7
82071  #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
82072  #endif
82073 #endif
82074 #ifdef __linux__
82075  #ifndef _BSD_SOURCE
82076  #define _BSD_SOURCE
82077  #endif
82078  #ifndef _DEFAULT_SOURCE
82079  #define _DEFAULT_SOURCE
82080  #endif
82081  #ifndef __USE_BSD
82082  #define __USE_BSD
82083  #endif
82084  #include <endian.h>
82085 #endif
82086 #include <stdlib.h>
82087 #include <string.h>
82088 #if !defined(MA_DR_FLAC_NO_SIMD)
82089  #if defined(MA_X64) || defined(MA_X86)
82090  #if defined(_MSC_VER) && !defined(__clang__)
82091  #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2)
82092  #define MA_DR_FLAC_SUPPORT_SSE2
82093  #endif
82094  #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41)
82095  #define MA_DR_FLAC_SUPPORT_SSE41
82096  #endif
82097  #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
82098  #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2)
82099  #define MA_DR_FLAC_SUPPORT_SSE2
82100  #endif
82101  #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41)
82102  #define MA_DR_FLAC_SUPPORT_SSE41
82103  #endif
82104  #endif
82105  #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
82106  #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include(<emmintrin.h>)
82107  #define MA_DR_FLAC_SUPPORT_SSE2
82108  #endif
82109  #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include(<smmintrin.h>)
82110  #define MA_DR_FLAC_SUPPORT_SSE41
82111  #endif
82112  #endif
82113  #if defined(MA_DR_FLAC_SUPPORT_SSE41)
82114  #include <smmintrin.h>
82115  #elif defined(MA_DR_FLAC_SUPPORT_SSE2)
82116  #include <emmintrin.h>
82117  #endif
82118  #endif
82119  #if defined(MA_ARM)
82120  #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
82121  #define MA_DR_FLAC_SUPPORT_NEON
82122  #include <arm_neon.h>
82123  #endif
82124  #endif
82125 #endif
82126 #if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64))
82127  #if defined(_MSC_VER) && !defined(__clang__)
82128  #if _MSC_VER >= 1400
82129  #include <intrin.h>
82130  static void ma_dr_flac__cpuid(int info[4], int fid)
82131  {
82132  __cpuid(info, fid);
82133  }
82134  #else
82135  #define MA_DR_FLAC_NO_CPUID
82136  #endif
82137  #else
82138  #if defined(__GNUC__) || defined(__clang__)
82139  static void ma_dr_flac__cpuid(int info[4], int fid)
82140  {
82141  #if defined(MA_X86) && defined(__PIC__)
82142  __asm__ __volatile__ (
82143  "xchg{l} {%%}ebx, %k1;"
82144  "cpuid;"
82145  "xchg{l} {%%}ebx, %k1;"
82146  : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
82147  );
82148  #else
82149  __asm__ __volatile__ (
82150  "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
82151  );
82152  #endif
82153  }
82154  #else
82155  #define MA_DR_FLAC_NO_CPUID
82156  #endif
82157  #endif
82158 #else
82159  #define MA_DR_FLAC_NO_CPUID
82160 #endif
82161 static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void)
82162 {
82163 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
82164  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2)
82165  #if defined(MA_X64)
82166  return MA_TRUE;
82167  #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
82168  return MA_TRUE;
82169  #else
82170  #if defined(MA_DR_FLAC_NO_CPUID)
82171  return MA_FALSE;
82172  #else
82173  int info[4];
82174  ma_dr_flac__cpuid(info, 1);
82175  return (info[3] & (1 << 26)) != 0;
82176  #endif
82177  #endif
82178  #else
82179  return MA_FALSE;
82180  #endif
82181 #else
82182  return MA_FALSE;
82183 #endif
82184 }
82185 static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void)
82186 {
82187 #if defined(MA_DR_FLAC_SUPPORT_SSE41)
82188  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41)
82189  #if defined(__SSE4_1__) || defined(__AVX__)
82190  return MA_TRUE;
82191  #else
82192  #if defined(MA_DR_FLAC_NO_CPUID)
82193  return MA_FALSE;
82194  #else
82195  int info[4];
82196  ma_dr_flac__cpuid(info, 1);
82197  return (info[2] & (1 << 19)) != 0;
82198  #endif
82199  #endif
82200  #else
82201  return MA_FALSE;
82202  #endif
82203 #else
82204  return MA_FALSE;
82205 #endif
82206 }
82207 #if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__)
82208  #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
82209 #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
82210  #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
82211 #elif defined(__clang__)
82212  #if defined(__has_builtin)
82213  #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
82214  #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
82215  #endif
82216  #endif
82217 #endif
82218 #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)
82219  #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
82220  #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
82221  #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
82222 #elif defined(__clang__)
82223  #if defined(__has_builtin)
82224  #if __has_builtin(__builtin_bswap16)
82225  #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
82226  #endif
82227  #if __has_builtin(__builtin_bswap32)
82228  #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
82229  #endif
82230  #if __has_builtin(__builtin_bswap64)
82231  #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
82232  #endif
82233  #endif
82234 #elif defined(__GNUC__)
82235  #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
82236  #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
82237  #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
82238  #endif
82239  #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
82240  #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
82241  #endif
82242 #elif defined(__WATCOMC__) && defined(__386__)
82243  #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
82244  #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
82245  #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
82246  extern __inline ma_uint16 _watcom_bswap16(ma_uint16);
82247  extern __inline ma_uint32 _watcom_bswap32(ma_uint32);
82248  extern __inline ma_uint64 _watcom_bswap64(ma_uint64);
82249 #pragma aux _watcom_bswap16 = \
82250  "xchg al, ah" \
82251  parm [ax] \
82252  value [ax] \
82253  modify nomemory;
82254 #pragma aux _watcom_bswap32 = \
82255  "bswap eax" \
82256  parm [eax] \
82257  value [eax] \
82258  modify nomemory;
82259 #pragma aux _watcom_bswap64 = \
82260  "bswap eax" \
82261  "bswap edx" \
82262  "xchg eax,edx" \
82263  parm [eax edx] \
82264  value [eax edx] \
82265  modify nomemory;
82266 #endif
82267 #ifndef MA_DR_FLAC_ASSERT
82268 #include <assert.h>
82269 #define MA_DR_FLAC_ASSERT(expression) assert(expression)
82270 #endif
82271 #ifndef MA_DR_FLAC_MALLOC
82272 #define MA_DR_FLAC_MALLOC(sz) malloc((sz))
82273 #endif
82274 #ifndef MA_DR_FLAC_REALLOC
82275 #define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz))
82276 #endif
82277 #ifndef MA_DR_FLAC_FREE
82278 #define MA_DR_FLAC_FREE(p) free((p))
82279 #endif
82280 #ifndef MA_DR_FLAC_COPY_MEMORY
82281 #define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
82282 #endif
82283 #ifndef MA_DR_FLAC_ZERO_MEMORY
82284 #define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
82285 #endif
82286 #ifndef MA_DR_FLAC_ZERO_OBJECT
82287 #define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p)))
82288 #endif
82289 #define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64
82290 #define MA_DR_FLAC_SUBFRAME_CONSTANT 0
82291 #define MA_DR_FLAC_SUBFRAME_VERBATIM 1
82292 #define MA_DR_FLAC_SUBFRAME_FIXED 8
82293 #define MA_DR_FLAC_SUBFRAME_LPC 32
82294 #define MA_DR_FLAC_SUBFRAME_RESERVED 255
82295 #define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
82296 #define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
82297 #define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
82298 #define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
82299 #define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
82300 #define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
82301 #define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18
82302 #define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36
82303 #define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12
82304 #define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
82305 MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
82306 {
82307  if (pMajor) {
82308  *pMajor = MA_DR_FLAC_VERSION_MAJOR;
82309  }
82310  if (pMinor) {
82311  *pMinor = MA_DR_FLAC_VERSION_MINOR;
82312  }
82313  if (pRevision) {
82314  *pRevision = MA_DR_FLAC_VERSION_REVISION;
82315  }
82316 }
82317 MA_API const char* ma_dr_flac_version_string(void)
82318 {
82319  return MA_DR_FLAC_VERSION_STRING;
82320 }
82321 #if defined(__has_feature)
82322  #if __has_feature(thread_sanitizer)
82323  #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
82324  #else
82325  #define MA_DR_FLAC_NO_THREAD_SANITIZE
82326  #endif
82327 #else
82328  #define MA_DR_FLAC_NO_THREAD_SANITIZE
82329 #endif
82330 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
82331 static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE;
82332 #endif
82333 #ifndef MA_DR_FLAC_NO_CPUID
82334 static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE;
82335 static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE;
82336 MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void)
82337 {
82338  static ma_bool32 isCPUCapsInitialized = MA_FALSE;
82339  if (!isCPUCapsInitialized) {
82340 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
82341  int info[4] = {0};
82342  ma_dr_flac__cpuid(info, 0x80000001);
82343  ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
82344 #endif
82345  ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2();
82346  ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41();
82347  isCPUCapsInitialized = MA_TRUE;
82348  }
82349 }
82350 #else
82351 static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE;
82352 static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void)
82353 {
82354 #if defined(MA_DR_FLAC_SUPPORT_NEON)
82355  #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON)
82356  #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
82357  return MA_TRUE;
82358  #else
82359  return MA_FALSE;
82360  #endif
82361  #else
82362  return MA_FALSE;
82363  #endif
82364 #else
82365  return MA_FALSE;
82366 #endif
82367 }
82368 MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void)
82369 {
82370  ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon();
82371 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
82372  ma_dr_flac__gIsLZCNTSupported = MA_TRUE;
82373 #endif
82374 }
82375 #endif
82376 static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void)
82377 {
82378 #if defined(MA_X86) || defined(MA_X64)
82379  return MA_TRUE;
82380 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
82381  return MA_TRUE;
82382 #else
82383  int n = 1;
82384  return (*(char*)&n) == 1;
82385 #endif
82386 }
82387 static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n)
82388 {
82389 #ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
82390  #if defined(_MSC_VER) && !defined(__clang__)
82391  return _byteswap_ushort(n);
82392  #elif defined(__GNUC__) || defined(__clang__)
82393  return __builtin_bswap16(n);
82394  #elif defined(__WATCOMC__) && defined(__386__)
82395  return _watcom_bswap16(n);
82396  #else
82397  #error "This compiler does not support the byte swap intrinsic."
82398  #endif
82399 #else
82400  return ((n & 0xFF00) >> 8) |
82401  ((n & 0x00FF) << 8);
82402 #endif
82403 }
82404 static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n)
82405 {
82406 #ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
82407  #if defined(_MSC_VER) && !defined(__clang__)
82408  return _byteswap_ulong(n);
82409  #elif defined(__GNUC__) || defined(__clang__)
82410  #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT)
82411  ma_uint32 r;
82412  __asm__ __volatile__ (
82413  #if defined(MA_64BIT)
82414  "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
82415  #else
82416  "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
82417  #endif
82418  );
82419  return r;
82420  #else
82421  return __builtin_bswap32(n);
82422  #endif
82423  #elif defined(__WATCOMC__) && defined(__386__)
82424  return _watcom_bswap32(n);
82425  #else
82426  #error "This compiler does not support the byte swap intrinsic."
82427  #endif
82428 #else
82429  return ((n & 0xFF000000) >> 24) |
82430  ((n & 0x00FF0000) >> 8) |
82431  ((n & 0x0000FF00) << 8) |
82432  ((n & 0x000000FF) << 24);
82433 #endif
82434 }
82435 static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n)
82436 {
82437 #ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
82438  #if defined(_MSC_VER) && !defined(__clang__)
82439  return _byteswap_uint64(n);
82440  #elif defined(__GNUC__) || defined(__clang__)
82441  return __builtin_bswap64(n);
82442  #elif defined(__WATCOMC__) && defined(__386__)
82443  return _watcom_bswap64(n);
82444  #else
82445  #error "This compiler does not support the byte swap intrinsic."
82446  #endif
82447 #else
82448  return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) |
82449  ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) |
82450  ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) |
82451  ((n & ((ma_uint64)0x000000FF << 32)) >> 8) |
82452  ((n & ((ma_uint64)0xFF000000 )) << 8) |
82453  ((n & ((ma_uint64)0x00FF0000 )) << 24) |
82454  ((n & ((ma_uint64)0x0000FF00 )) << 40) |
82455  ((n & ((ma_uint64)0x000000FF )) << 56);
82456 #endif
82457 }
82458 static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n)
82459 {
82460  if (ma_dr_flac__is_little_endian()) {
82461  return ma_dr_flac__swap_endian_uint16(n);
82462  }
82463  return n;
82464 }
82465 static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n)
82466 {
82467  if (ma_dr_flac__is_little_endian()) {
82468  return ma_dr_flac__swap_endian_uint32(n);
82469  }
82470  return n;
82471 }
82472 static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData)
82473 {
82474  const ma_uint8* pNum = (ma_uint8*)pData;
82475  return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3);
82476 }
82477 static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n)
82478 {
82479  if (ma_dr_flac__is_little_endian()) {
82480  return ma_dr_flac__swap_endian_uint64(n);
82481  }
82482  return n;
82483 }
82484 static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n)
82485 {
82486  if (!ma_dr_flac__is_little_endian()) {
82487  return ma_dr_flac__swap_endian_uint32(n);
82488  }
82489  return n;
82490 }
82491 static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData)
82492 {
82493  const ma_uint8* pNum = (ma_uint8*)pData;
82494  return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24;
82495 }
82496 static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n)
82497 {
82498  ma_uint32 result = 0;
82499  result |= (n & 0x7F000000) >> 3;
82500  result |= (n & 0x007F0000) >> 2;
82501  result |= (n & 0x00007F00) >> 1;
82502  result |= (n & 0x0000007F) >> 0;
82503  return result;
82504 }
82505 static ma_uint8 ma_dr_flac__crc8_table[] = {
82506  0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
82507  0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
82508  0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
82509  0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
82510  0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
82511  0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
82512  0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
82513  0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
82514  0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
82515  0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
82516  0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
82517  0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
82518  0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
82519  0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
82520  0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
82521  0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
82522 };
82523 static ma_uint16 ma_dr_flac__crc16_table[] = {
82524  0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
82525  0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
82526  0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
82527  0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
82528  0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
82529  0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
82530  0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
82531  0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
82532  0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
82533  0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
82534  0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
82535  0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
82536  0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
82537  0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
82538  0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
82539  0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
82540  0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
82541  0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
82542  0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
82543  0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
82544  0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
82545  0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
82546  0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
82547  0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
82548  0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
82549  0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
82550  0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
82551  0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
82552  0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
82553  0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
82554  0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
82555  0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
82556 };
82557 static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data)
82558 {
82559  return ma_dr_flac__crc8_table[crc ^ data];
82560 }
82561 static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count)
82562 {
82563 #ifdef MA_DR_FLAC_NO_CRC
82564  (void)crc;
82565  (void)data;
82566  (void)count;
82567  return 0;
82568 #else
82569 #if 0
82570  ma_uint8 p = 0x07;
82571  for (int i = count-1; i >= 0; --i) {
82572  ma_uint8 bit = (data & (1 << i)) >> i;
82573  if (crc & 0x80) {
82574  crc = ((crc << 1) | bit) ^ p;
82575  } else {
82576  crc = ((crc << 1) | bit);
82577  }
82578  }
82579  return crc;
82580 #else
82581  ma_uint32 wholeBytes;
82582  ma_uint32 leftoverBits;
82583  ma_uint64 leftoverDataMask;
82584  static ma_uint64 leftoverDataMaskTable[8] = {
82585  0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
82586  };
82587  MA_DR_FLAC_ASSERT(count <= 32);
82588  wholeBytes = count >> 3;
82589  leftoverBits = count - (wholeBytes*8);
82590  leftoverDataMask = leftoverDataMaskTable[leftoverBits];
82591  switch (wholeBytes) {
82592  case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
82593  case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
82594  case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
82595  case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
82596  case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]);
82597  }
82598  return crc;
82599 #endif
82600 #endif
82601 }
82602 static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data)
82603 {
82604  return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data];
82605 }
82606 static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data)
82607 {
82608 #ifdef MA_64BIT
82609  crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF));
82610  crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF));
82611  crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF));
82612  crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF));
82613 #endif
82614  crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF));
82615  crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF));
82616  crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF));
82617  crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF));
82618  return crc;
82619 }
82620 static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount)
82621 {
82622  switch (byteCount)
82623  {
82624 #ifdef MA_64BIT
82625  case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF));
82626  case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF));
82627  case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF));
82628  case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF));
82629 #endif
82630  case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF));
82631  case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF));
82632  case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF));
82633  case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF));
82634  }
82635  return crc;
82636 }
82637 #if 0
82638 static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count)
82639 {
82640 #ifdef MA_DR_FLAC_NO_CRC
82641  (void)crc;
82642  (void)data;
82643  (void)count;
82644  return 0;
82645 #else
82646 #if 0
82647  ma_uint16 p = 0x8005;
82648  for (int i = count-1; i >= 0; --i) {
82649  ma_uint16 bit = (data & (1ULL << i)) >> i;
82650  if (r & 0x8000) {
82651  r = ((r << 1) | bit) ^ p;
82652  } else {
82653  r = ((r << 1) | bit);
82654  }
82655  }
82656  return crc;
82657 #else
82658  ma_uint32 wholeBytes;
82659  ma_uint32 leftoverBits;
82660  ma_uint64 leftoverDataMask;
82661  static ma_uint64 leftoverDataMaskTable[8] = {
82662  0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
82663  };
82664  MA_DR_FLAC_ASSERT(count <= 64);
82665  wholeBytes = count >> 3;
82666  leftoverBits = count & 7;
82667  leftoverDataMask = leftoverDataMaskTable[leftoverBits];
82668  switch (wholeBytes) {
82669  default:
82670  case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
82671  case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
82672  case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
82673  case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
82674  case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
82675  }
82676  return crc;
82677 #endif
82678 #endif
82679 }
82680 static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count)
82681 {
82682 #ifdef MA_DR_FLAC_NO_CRC
82683  (void)crc;
82684  (void)data;
82685  (void)count;
82686  return 0;
82687 #else
82688  ma_uint32 wholeBytes;
82689  ma_uint32 leftoverBits;
82690  ma_uint64 leftoverDataMask;
82691  static ma_uint64 leftoverDataMaskTable[8] = {
82692  0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
82693  };
82694  MA_DR_FLAC_ASSERT(count <= 64);
82695  wholeBytes = count >> 3;
82696  leftoverBits = count & 7;
82697  leftoverDataMask = leftoverDataMaskTable[leftoverBits];
82698  switch (wholeBytes) {
82699  default:
82700  case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits)));
82701  case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
82702  case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
82703  case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
82704  case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
82705  case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
82706  case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
82707  case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits)));
82708  case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
82709  }
82710  return crc;
82711 #endif
82712 }
82713 static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count)
82714 {
82715 #ifdef MA_64BIT
82716  return ma_dr_flac_crc16__64bit(crc, data, count);
82717 #else
82718  return ma_dr_flac_crc16__32bit(crc, data, count);
82719 #endif
82720 }
82721 #endif
82722 #ifdef MA_64BIT
82723 #define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64
82724 #else
82725 #define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32
82726 #endif
82727 #define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
82728 #define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
82729 #define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
82730 #define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount)))
82731 #define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
82732 #define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount))
82733 #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)))
82734 #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)))
82735 #define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
82736 #define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
82737 #define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
82738 #ifndef MA_DR_FLAC_NO_CRC
82739 static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs)
82740 {
82741  bs->crc16 = 0;
82742  bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
82743 }
82744 static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs)
82745 {
82746  if (bs->crc16CacheIgnoredBytes == 0) {
82747  bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache);
82748  } else {
82749  bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
82750  bs->crc16CacheIgnoredBytes = 0;
82751  }
82752 }
82753 static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs)
82754 {
82755  MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
82756  if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
82757  ma_dr_flac__update_crc16(bs);
82758  } else {
82759  bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
82760  bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
82761  }
82762  return bs->crc16;
82763 }
82764 #endif
82765 static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs)
82766 {
82767  size_t bytesRead;
82768  size_t alignedL1LineCount;
82769  if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
82770  bs->cache = bs->cacheL2[bs->nextL2Line++];
82771  return MA_TRUE;
82772  }
82773  if (bs->unalignedByteCount > 0) {
82774  return MA_FALSE;
82775  }
82776  bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs));
82777  bs->nextL2Line = 0;
82778  if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) {
82779  bs->cache = bs->cacheL2[bs->nextL2Line++];
82780  return MA_TRUE;
82781  }
82782  alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs);
82783  bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs));
82784  if (bs->unalignedByteCount > 0) {
82785  bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
82786  }
82787  if (alignedL1LineCount > 0) {
82788  size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
82789  size_t i;
82790  for (i = alignedL1LineCount; i > 0; --i) {
82791  bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
82792  }
82793  bs->nextL2Line = (ma_uint32)offset;
82794  bs->cache = bs->cacheL2[bs->nextL2Line++];
82795  return MA_TRUE;
82796  } else {
82797  bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs);
82798  return MA_FALSE;
82799  }
82800 }
82801 static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs)
82802 {
82803  size_t bytesRead;
82804 #ifndef MA_DR_FLAC_NO_CRC
82805  ma_dr_flac__update_crc16(bs);
82806 #endif
82807  if (ma_dr_flac__reload_l1_cache_from_l2(bs)) {
82808  bs->cache = ma_dr_flac__be2host__cache_line(bs->cache);
82809  bs->consumedBits = 0;
82810 #ifndef MA_DR_FLAC_NO_CRC
82811  bs->crc16Cache = bs->cache;
82812 #endif
82813  return MA_TRUE;
82814  }
82815  bytesRead = bs->unalignedByteCount;
82816  if (bytesRead == 0) {
82817  bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
82818  return MA_FALSE;
82819  }
82820  MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs));
82821  bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
82822  bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache);
82823  bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs));
82824  bs->unalignedByteCount = 0;
82825 #ifndef MA_DR_FLAC_NO_CRC
82826  bs->crc16Cache = bs->cache >> bs->consumedBits;
82827  bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
82828 #endif
82829  return MA_TRUE;
82830 }
82831 static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs)
82832 {
82833  bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs);
82834  bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
82835  bs->cache = 0;
82836  bs->unalignedByteCount = 0;
82837  bs->unalignedCache = 0;
82838 #ifndef MA_DR_FLAC_NO_CRC
82839  bs->crc16Cache = 0;
82840  bs->crc16CacheIgnoredBytes = 0;
82841 #endif
82842 }
82843 static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut)
82844 {
82845  MA_DR_FLAC_ASSERT(bs != NULL);
82846  MA_DR_FLAC_ASSERT(pResultOut != NULL);
82847  MA_DR_FLAC_ASSERT(bitCount > 0);
82848  MA_DR_FLAC_ASSERT(bitCount <= 32);
82849  if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
82850  if (!ma_dr_flac__reload_cache(bs)) {
82851  return MA_FALSE;
82852  }
82853  }
82854  if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
82855 #ifdef MA_64BIT
82856  *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
82857  bs->consumedBits += bitCount;
82858  bs->cache <<= bitCount;
82859 #else
82860  if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
82861  *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
82862  bs->consumedBits += bitCount;
82863  bs->cache <<= bitCount;
82864  } else {
82865  *pResultOut = (ma_uint32)bs->cache;
82866  bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
82867  bs->cache = 0;
82868  }
82869 #endif
82870  return MA_TRUE;
82871  } else {
82872  ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
82873  ma_uint32 bitCountLo = bitCount - bitCountHi;
82874  ma_uint32 resultHi;
82875  MA_DR_FLAC_ASSERT(bitCountHi > 0);
82876  MA_DR_FLAC_ASSERT(bitCountHi < 32);
82877  resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
82878  if (!ma_dr_flac__reload_cache(bs)) {
82879  return MA_FALSE;
82880  }
82881  if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
82882  return MA_FALSE;
82883  }
82884  *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
82885  bs->consumedBits += bitCountLo;
82886  bs->cache <<= bitCountLo;
82887  return MA_TRUE;
82888  }
82889 }
82890 static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult)
82891 {
82892  ma_uint32 result;
82893  MA_DR_FLAC_ASSERT(bs != NULL);
82894  MA_DR_FLAC_ASSERT(pResult != NULL);
82895  MA_DR_FLAC_ASSERT(bitCount > 0);
82896  MA_DR_FLAC_ASSERT(bitCount <= 32);
82897  if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
82898  return MA_FALSE;
82899  }
82900  if (bitCount < 32) {
82901  ma_uint32 signbit;
82902  signbit = ((result >> (bitCount-1)) & 0x01);
82903  result |= (~signbit + 1) << bitCount;
82904  }
82905  *pResult = (ma_int32)result;
82906  return MA_TRUE;
82907 }
82908 #ifdef MA_64BIT
82909 static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut)
82910 {
82911  ma_uint32 resultHi;
82912  ma_uint32 resultLo;
82913  MA_DR_FLAC_ASSERT(bitCount <= 64);
82914  MA_DR_FLAC_ASSERT(bitCount > 32);
82915  if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) {
82916  return MA_FALSE;
82917  }
82918  if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) {
82919  return MA_FALSE;
82920  }
82921  *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo);
82922  return MA_TRUE;
82923 }
82924 #endif
82925 #if 0
82926 static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut)
82927 {
82928  ma_uint64 result;
82929  ma_uint64 signbit;
82930  MA_DR_FLAC_ASSERT(bitCount <= 64);
82931  if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) {
82932  return MA_FALSE;
82933  }
82934  signbit = ((result >> (bitCount-1)) & 0x01);
82935  result |= (~signbit + 1) << bitCount;
82936  *pResultOut = (ma_int64)result;
82937  return MA_TRUE;
82938 }
82939 #endif
82940 static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult)
82941 {
82942  ma_uint32 result;
82943  MA_DR_FLAC_ASSERT(bs != NULL);
82944  MA_DR_FLAC_ASSERT(pResult != NULL);
82945  MA_DR_FLAC_ASSERT(bitCount > 0);
82946  MA_DR_FLAC_ASSERT(bitCount <= 16);
82947  if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
82948  return MA_FALSE;
82949  }
82950  *pResult = (ma_uint16)result;
82951  return MA_TRUE;
82952 }
82953 #if 0
82954 static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult)
82955 {
82956  ma_int32 result;
82957  MA_DR_FLAC_ASSERT(bs != NULL);
82958  MA_DR_FLAC_ASSERT(pResult != NULL);
82959  MA_DR_FLAC_ASSERT(bitCount > 0);
82960  MA_DR_FLAC_ASSERT(bitCount <= 16);
82961  if (!ma_dr_flac__read_int32(bs, bitCount, &result)) {
82962  return MA_FALSE;
82963  }
82964  *pResult = (ma_int16)result;
82965  return MA_TRUE;
82966 }
82967 #endif
82968 static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult)
82969 {
82970  ma_uint32 result;
82971  MA_DR_FLAC_ASSERT(bs != NULL);
82972  MA_DR_FLAC_ASSERT(pResult != NULL);
82973  MA_DR_FLAC_ASSERT(bitCount > 0);
82974  MA_DR_FLAC_ASSERT(bitCount <= 8);
82975  if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
82976  return MA_FALSE;
82977  }
82978  *pResult = (ma_uint8)result;
82979  return MA_TRUE;
82980 }
82981 static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult)
82982 {
82983  ma_int32 result;
82984  MA_DR_FLAC_ASSERT(bs != NULL);
82985  MA_DR_FLAC_ASSERT(pResult != NULL);
82986  MA_DR_FLAC_ASSERT(bitCount > 0);
82987  MA_DR_FLAC_ASSERT(bitCount <= 8);
82988  if (!ma_dr_flac__read_int32(bs, bitCount, &result)) {
82989  return MA_FALSE;
82990  }
82991  *pResult = (ma_int8)result;
82992  return MA_TRUE;
82993 }
82994 static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek)
82995 {
82996  if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
82997  bs->consumedBits += (ma_uint32)bitsToSeek;
82998  bs->cache <<= bitsToSeek;
82999  return MA_TRUE;
83000  } else {
83001  bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
83002  bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
83003  bs->cache = 0;
83004 #ifdef MA_64BIT
83005  while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
83006  ma_uint64 bin;
83007  if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
83008  return MA_FALSE;
83009  }
83010  bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
83011  }
83012 #else
83013  while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
83014  ma_uint32 bin;
83015  if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
83016  return MA_FALSE;
83017  }
83018  bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
83019  }
83020 #endif
83021  while (bitsToSeek >= 8) {
83022  ma_uint8 bin;
83023  if (!ma_dr_flac__read_uint8(bs, 8, &bin)) {
83024  return MA_FALSE;
83025  }
83026  bitsToSeek -= 8;
83027  }
83028  if (bitsToSeek > 0) {
83029  ma_uint8 bin;
83030  if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) {
83031  return MA_FALSE;
83032  }
83033  bitsToSeek = 0;
83034  }
83035  MA_DR_FLAC_ASSERT(bitsToSeek == 0);
83036  return MA_TRUE;
83037  }
83038 }
83039 static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs)
83040 {
83041  MA_DR_FLAC_ASSERT(bs != NULL);
83042  if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
83043  return MA_FALSE;
83044  }
83045  for (;;) {
83046  ma_uint8 hi;
83047 #ifndef MA_DR_FLAC_NO_CRC
83048  ma_dr_flac__reset_crc16(bs);
83049 #endif
83050  if (!ma_dr_flac__read_uint8(bs, 8, &hi)) {
83051  return MA_FALSE;
83052  }
83053  if (hi == 0xFF) {
83054  ma_uint8 lo;
83055  if (!ma_dr_flac__read_uint8(bs, 6, &lo)) {
83056  return MA_FALSE;
83057  }
83058  if (lo == 0x3E) {
83059  return MA_TRUE;
83060  } else {
83061  if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
83062  return MA_FALSE;
83063  }
83064  }
83065  }
83066  }
83067 }
83068 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
83069 #define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
83070 #endif
83071 #if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__)
83072 #define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
83073 #endif
83074 #if defined(__WATCOMC__) && defined(__386__)
83075 #define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM
83076 #endif
83077 #ifdef __MRC__
83078 #include <intrinsics.h>
83079 #define MA_DR_FLAC_IMPLEMENT_CLZ_MRC
83080 #endif
83081 static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x)
83082 {
83083  ma_uint32 n;
83084  static ma_uint32 clz_table_4[] = {
83085  0,
83086  4,
83087  3, 3,
83088  2, 2, 2, 2,
83089  1, 1, 1, 1, 1, 1, 1, 1
83090  };
83091  if (x == 0) {
83092  return sizeof(x)*8;
83093  }
83094  n = clz_table_4[x >> (sizeof(x)*8 - 4)];
83095  if (n == 0) {
83096 #ifdef MA_64BIT
83097  if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
83098  if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
83099  if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
83100  if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; }
83101 #else
83102  if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
83103  if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
83104  if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
83105 #endif
83106  n += clz_table_4[x >> (sizeof(x)*8 - 4)];
83107  }
83108  return n - 1;
83109 }
83110 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
83111 static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void)
83112 {
83113 #if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
83114  return MA_TRUE;
83115 #elif defined(__MRC__)
83116  return MA_TRUE;
83117 #else
83118  #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC
83119  return ma_dr_flac__gIsLZCNTSupported;
83120  #else
83121  return MA_FALSE;
83122  #endif
83123 #endif
83124 }
83125 static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x)
83126 {
83127 #if defined(_MSC_VER)
83128  #ifdef MA_64BIT
83129  return (ma_uint32)__lzcnt64(x);
83130  #else
83131  return (ma_uint32)__lzcnt(x);
83132  #endif
83133 #else
83134  #if defined(__GNUC__) || defined(__clang__)
83135  #if defined(MA_X64)
83136  {
83137  ma_uint64 r;
83138  __asm__ __volatile__ (
83139  "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
83140  );
83141  return (ma_uint32)r;
83142  }
83143  #elif defined(MA_X86)
83144  {
83145  ma_uint32 r;
83146  __asm__ __volatile__ (
83147  "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
83148  );
83149  return r;
83150  }
83151  #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT)
83152  {
83153  unsigned int r;
83154  __asm__ __volatile__ (
83155  #if defined(MA_64BIT)
83156  "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x)
83157  #else
83158  "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x)
83159  #endif
83160  );
83161  return r;
83162  }
83163  #else
83164  if (x == 0) {
83165  return sizeof(x)*8;
83166  }
83167  #ifdef MA_64BIT
83168  return (ma_uint32)__builtin_clzll((ma_uint64)x);
83169  #else
83170  return (ma_uint32)__builtin_clzl((ma_uint32)x);
83171  #endif
83172  #endif
83173  #else
83174  #error "This compiler does not support the lzcnt intrinsic."
83175  #endif
83176 #endif
83177 }
83178 #endif
83179 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
83180 #include <intrin.h>
83181 static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x)
83182 {
83183  ma_uint32 n;
83184  if (x == 0) {
83185  return sizeof(x)*8;
83186  }
83187 #ifdef MA_64BIT
83188  _BitScanReverse64((unsigned long*)&n, x);
83189 #else
83190  _BitScanReverse((unsigned long*)&n, x);
83191 #endif
83192  return sizeof(x)*8 - n - 1;
83193 }
83194 #endif
83195 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM
83196 static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32);
83197 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT
83198 #pragma aux ma_dr_flac__clz_watcom_lzcnt = \
83199  "db 0F3h, 0Fh, 0BDh, 0C0h" \
83200  parm [eax] \
83201  value [eax] \
83202  modify nomemory;
83203 #else
83204 #pragma aux ma_dr_flac__clz_watcom = \
83205  "bsr eax, eax" \
83206  "xor eax, 31" \
83207  parm [eax] nomemory \
83208  value [eax] \
83209  modify exact [eax] nomemory;
83210 #endif
83211 #endif
83212 static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x)
83213 {
83214 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
83215  if (ma_dr_flac__is_lzcnt_supported()) {
83216  return ma_dr_flac__clz_lzcnt(x);
83217  } else
83218 #endif
83219  {
83220 #ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
83221  return ma_dr_flac__clz_msvc(x);
83222 #elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT)
83223  return ma_dr_flac__clz_watcom_lzcnt(x);
83224 #elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM)
83225  return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x);
83226 #elif defined(__MRC__)
83227  return __cntlzw(x);
83228 #else
83229  return ma_dr_flac__clz_software(x);
83230 #endif
83231  }
83232 }
83233 static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut)
83234 {
83235  ma_uint32 zeroCounter = 0;
83236  ma_uint32 setBitOffsetPlus1;
83237  while (bs->cache == 0) {
83238  zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
83239  if (!ma_dr_flac__reload_cache(bs)) {
83240  return MA_FALSE;
83241  }
83242  }
83243  if (bs->cache == 1) {
83244  *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1;
83245  if (!ma_dr_flac__reload_cache(bs)) {
83246  return MA_FALSE;
83247  }
83248  return MA_TRUE;
83249  }
83250  setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache);
83251  setBitOffsetPlus1 += 1;
83252  if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
83253  return MA_FALSE;
83254  }
83255  bs->consumedBits += setBitOffsetPlus1;
83256  bs->cache <<= setBitOffsetPlus1;
83257  *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
83258  return MA_TRUE;
83259 }
83260 static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart)
83261 {
83262  MA_DR_FLAC_ASSERT(bs != NULL);
83263  MA_DR_FLAC_ASSERT(offsetFromStart > 0);
83264  if (offsetFromStart > 0x7FFFFFFF) {
83265  ma_uint64 bytesRemaining = offsetFromStart;
83266  if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) {
83267  return MA_FALSE;
83268  }
83269  bytesRemaining -= 0x7FFFFFFF;
83270  while (bytesRemaining > 0x7FFFFFFF) {
83271  if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) {
83272  return MA_FALSE;
83273  }
83274  bytesRemaining -= 0x7FFFFFFF;
83275  }
83276  if (bytesRemaining > 0) {
83277  if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) {
83278  return MA_FALSE;
83279  }
83280  }
83281  } else {
83282  if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) {
83283  return MA_FALSE;
83284  }
83285  }
83286  ma_dr_flac__reset_cache(bs);
83287  return MA_TRUE;
83288 }
83289 static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut)
83290 {
83291  ma_uint8 crc;
83292  ma_uint64 result;
83293  ma_uint8 utf8[7] = {0};
83294  int byteCount;
83295  int i;
83296  MA_DR_FLAC_ASSERT(bs != NULL);
83297  MA_DR_FLAC_ASSERT(pNumberOut != NULL);
83298  MA_DR_FLAC_ASSERT(pCRCOut != NULL);
83299  crc = *pCRCOut;
83300  if (!ma_dr_flac__read_uint8(bs, 8, utf8)) {
83301  *pNumberOut = 0;
83302  return MA_AT_END;
83303  }
83304  crc = ma_dr_flac_crc8(crc, utf8[0], 8);
83305  if ((utf8[0] & 0x80) == 0) {
83306  *pNumberOut = utf8[0];
83307  *pCRCOut = crc;
83308  return MA_SUCCESS;
83309  }
83310  if ((utf8[0] & 0xE0) == 0xC0) {
83311  byteCount = 2;
83312  } else if ((utf8[0] & 0xF0) == 0xE0) {
83313  byteCount = 3;
83314  } else if ((utf8[0] & 0xF8) == 0xF0) {
83315  byteCount = 4;
83316  } else if ((utf8[0] & 0xFC) == 0xF8) {
83317  byteCount = 5;
83318  } else if ((utf8[0] & 0xFE) == 0xFC) {
83319  byteCount = 6;
83320  } else if ((utf8[0] & 0xFF) == 0xFE) {
83321  byteCount = 7;
83322  } else {
83323  *pNumberOut = 0;
83324  return MA_CRC_MISMATCH;
83325  }
83326  MA_DR_FLAC_ASSERT(byteCount > 1);
83327  result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
83328  for (i = 1; i < byteCount; ++i) {
83329  if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) {
83330  *pNumberOut = 0;
83331  return MA_AT_END;
83332  }
83333  crc = ma_dr_flac_crc8(crc, utf8[i], 8);
83334  result = (result << 6) | (utf8[i] & 0x3F);
83335  }
83336  *pNumberOut = result;
83337  *pCRCOut = crc;
83338  return MA_SUCCESS;
83339 }
83340 static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x)
83341 {
83342 #if 1
83343  ma_uint32 result = 0;
83344  while (x > 0) {
83345  result += 1;
83346  x >>= 1;
83347  }
83348  return result;
83349 #endif
83350 }
83351 static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision)
83352 {
83353  return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32;
83354 }
83355 #if defined(__clang__)
83356 __attribute__((no_sanitize("signed-integer-overflow")))
83357 #endif
83358 static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples)
83359 {
83360  ma_int32 prediction = 0;
83361  MA_DR_FLAC_ASSERT(order <= 32);
83362  switch (order)
83363  {
83364  case 32: prediction += coefficients[31] * pDecodedSamples[-32];
83365  case 31: prediction += coefficients[30] * pDecodedSamples[-31];
83366  case 30: prediction += coefficients[29] * pDecodedSamples[-30];
83367  case 29: prediction += coefficients[28] * pDecodedSamples[-29];
83368  case 28: prediction += coefficients[27] * pDecodedSamples[-28];
83369  case 27: prediction += coefficients[26] * pDecodedSamples[-27];
83370  case 26: prediction += coefficients[25] * pDecodedSamples[-26];
83371  case 25: prediction += coefficients[24] * pDecodedSamples[-25];
83372  case 24: prediction += coefficients[23] * pDecodedSamples[-24];
83373  case 23: prediction += coefficients[22] * pDecodedSamples[-23];
83374  case 22: prediction += coefficients[21] * pDecodedSamples[-22];
83375  case 21: prediction += coefficients[20] * pDecodedSamples[-21];
83376  case 20: prediction += coefficients[19] * pDecodedSamples[-20];
83377  case 19: prediction += coefficients[18] * pDecodedSamples[-19];
83378  case 18: prediction += coefficients[17] * pDecodedSamples[-18];
83379  case 17: prediction += coefficients[16] * pDecodedSamples[-17];
83380  case 16: prediction += coefficients[15] * pDecodedSamples[-16];
83381  case 15: prediction += coefficients[14] * pDecodedSamples[-15];
83382  case 14: prediction += coefficients[13] * pDecodedSamples[-14];
83383  case 13: prediction += coefficients[12] * pDecodedSamples[-13];
83384  case 12: prediction += coefficients[11] * pDecodedSamples[-12];
83385  case 11: prediction += coefficients[10] * pDecodedSamples[-11];
83386  case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
83387  case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
83388  case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
83389  case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
83390  case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
83391  case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
83392  case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
83393  case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
83394  case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
83395  case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
83396  }
83397  return (ma_int32)(prediction >> shift);
83398 }
83399 static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples)
83400 {
83401  ma_int64 prediction;
83402  MA_DR_FLAC_ASSERT(order <= 32);
83403 #ifndef MA_64BIT
83404  if (order == 8)
83405  {
83406  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83407  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83408  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83409  prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
83410  prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
83411  prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
83412  prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
83413  prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
83414  }
83415  else if (order == 7)
83416  {
83417  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83418  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83419  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83420  prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
83421  prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
83422  prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
83423  prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
83424  }
83425  else if (order == 3)
83426  {
83427  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83428  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83429  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83430  }
83431  else if (order == 6)
83432  {
83433  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83434  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83435  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83436  prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
83437  prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
83438  prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
83439  }
83440  else if (order == 5)
83441  {
83442  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83443  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83444  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83445  prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
83446  prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
83447  }
83448  else if (order == 4)
83449  {
83450  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83451  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83452  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83453  prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
83454  }
83455  else if (order == 12)
83456  {
83457  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83458  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83459  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83460  prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
83461  prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
83462  prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
83463  prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
83464  prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
83465  prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
83466  prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
83467  prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
83468  prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12];
83469  }
83470  else if (order == 2)
83471  {
83472  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83473  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83474  }
83475  else if (order == 1)
83476  {
83477  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83478  }
83479  else if (order == 10)
83480  {
83481  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83482  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83483  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83484  prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
83485  prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
83486  prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
83487  prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
83488  prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
83489  prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
83490  prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
83491  }
83492  else if (order == 9)
83493  {
83494  prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
83495  prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
83496  prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
83497  prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
83498  prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
83499  prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
83500  prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
83501  prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
83502  prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
83503  }
83504  else if (order == 11)
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  prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
83516  prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
83517  }
83518  else
83519  {
83520  int j;
83521  prediction = 0;
83522  for (j = 0; j < (int)order; ++j) {
83523  prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1];
83524  }
83525  }
83526 #endif
83527 #ifdef MA_64BIT
83528  prediction = 0;
83529  switch (order)
83530  {
83531  case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32];
83532  case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31];
83533  case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30];
83534  case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29];
83535  case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28];
83536  case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27];
83537  case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26];
83538  case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25];
83539  case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24];
83540  case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23];
83541  case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22];
83542  case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21];
83543  case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20];
83544  case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19];
83545  case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18];
83546  case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17];
83547  case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16];
83548  case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15];
83549  case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14];
83550  case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13];
83551  case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12];
83552  case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
83553  case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10];
83554  case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9];
83555  case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8];
83556  case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7];
83557  case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6];
83558  case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5];
83559  case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4];
83560  case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3];
83561  case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2];
83562  case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1];
83563  }
83564 #endif
83565  return (ma_int32)(prediction >> shift);
83566 }
83567 #if 0
83568 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)
83569 {
83570  ma_uint32 i;
83571  MA_DR_FLAC_ASSERT(bs != NULL);
83572  MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
83573  for (i = 0; i < count; ++i) {
83574  ma_uint32 zeroCounter = 0;
83575  for (;;) {
83576  ma_uint8 bit;
83577  if (!ma_dr_flac__read_uint8(bs, 1, &bit)) {
83578  return MA_FALSE;
83579  }
83580  if (bit == 0) {
83581  zeroCounter += 1;
83582  } else {
83583  break;
83584  }
83585  }
83586  ma_uint32 decodedRice;
83587  if (riceParam > 0) {
83588  if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) {
83589  return MA_FALSE;
83590  }
83591  } else {
83592  decodedRice = 0;
83593  }
83594  decodedRice |= (zeroCounter << riceParam);
83595  if ((decodedRice & 0x01)) {
83596  decodedRice = ~(decodedRice >> 1);
83597  } else {
83598  decodedRice = (decodedRice >> 1);
83599  }
83600  if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
83601  pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
83602  } else {
83603  pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
83604  }
83605  }
83606  return MA_TRUE;
83607 }
83608 #endif
83609 #if 0
83610 static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)
83611 {
83612  ma_uint32 zeroCounter = 0;
83613  ma_uint32 decodedRice;
83614  for (;;) {
83615  ma_uint8 bit;
83616  if (!ma_dr_flac__read_uint8(bs, 1, &bit)) {
83617  return MA_FALSE;
83618  }
83619  if (bit == 0) {
83620  zeroCounter += 1;
83621  } else {
83622  break;
83623  }
83624  }
83625  if (riceParam > 0) {
83626  if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) {
83627  return MA_FALSE;
83628  }
83629  } else {
83630  decodedRice = 0;
83631  }
83632  *pZeroCounterOut = zeroCounter;
83633  *pRiceParamPartOut = decodedRice;
83634  return MA_TRUE;
83635 }
83636 #endif
83637 #if 0
83638 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)
83639 {
83640  ma_dr_flac_cache_t riceParamMask;
83641  ma_uint32 zeroCounter;
83642  ma_uint32 setBitOffsetPlus1;
83643  ma_uint32 riceParamPart;
83644  ma_uint32 riceLength;
83645  MA_DR_FLAC_ASSERT(riceParam > 0);
83646  riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam);
83647  zeroCounter = 0;
83648  while (bs->cache == 0) {
83649  zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
83650  if (!ma_dr_flac__reload_cache(bs)) {
83651  return MA_FALSE;
83652  }
83653  }
83654  setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache);
83655  zeroCounter += setBitOffsetPlus1;
83656  setBitOffsetPlus1 += 1;
83657  riceLength = setBitOffsetPlus1 + riceParam;
83658  if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
83659  riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
83660  bs->consumedBits += riceLength;
83661  bs->cache <<= riceLength;
83662  } else {
83663  ma_uint32 bitCountLo;
83664  ma_dr_flac_cache_t resultHi;
83665  bs->consumedBits += riceLength;
83666  bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1);
83667  bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
83668  resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam);
83669  if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
83670 #ifndef MA_DR_FLAC_NO_CRC
83671  ma_dr_flac__update_crc16(bs);
83672 #endif
83673  bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83674  bs->consumedBits = 0;
83675 #ifndef MA_DR_FLAC_NO_CRC
83676  bs->crc16Cache = bs->cache;
83677 #endif
83678  } else {
83679  if (!ma_dr_flac__reload_cache(bs)) {
83680  return MA_FALSE;
83681  }
83682  if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
83683  return MA_FALSE;
83684  }
83685  }
83686  riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
83687  bs->consumedBits += bitCountLo;
83688  bs->cache <<= bitCountLo;
83689  }
83690  pZeroCounterOut[0] = zeroCounter;
83691  pRiceParamPartOut[0] = riceParamPart;
83692  return MA_TRUE;
83693 }
83694 #endif
83695 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)
83696 {
83697  ma_uint32 riceParamPlus1 = riceParam + 1;
83698  ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
83699  ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
83700  ma_dr_flac_cache_t bs_cache = bs->cache;
83701  ma_uint32 bs_consumedBits = bs->consumedBits;
83702  ma_uint32 lzcount = ma_dr_flac__clz(bs_cache);
83703  if (lzcount < sizeof(bs_cache)*8) {
83704  pZeroCounterOut[0] = lzcount;
83705  extract_rice_param_part:
83706  bs_cache <<= lzcount;
83707  bs_consumedBits += lzcount;
83708  if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
83709  pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift);
83710  bs_cache <<= riceParamPlus1;
83711  bs_consumedBits += riceParamPlus1;
83712  } else {
83713  ma_uint32 riceParamPartHi;
83714  ma_uint32 riceParamPartLo;
83715  ma_uint32 riceParamPartLoBitCount;
83716  riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift);
83717  riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
83718  MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
83719  if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
83720  #ifndef MA_DR_FLAC_NO_CRC
83721  ma_dr_flac__update_crc16(bs);
83722  #endif
83723  bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83724  bs_consumedBits = riceParamPartLoBitCount;
83725  #ifndef MA_DR_FLAC_NO_CRC
83726  bs->crc16Cache = bs_cache;
83727  #endif
83728  } else {
83729  if (!ma_dr_flac__reload_cache(bs)) {
83730  return MA_FALSE;
83731  }
83732  if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
83733  return MA_FALSE;
83734  }
83735  bs_cache = bs->cache;
83736  bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
83737  }
83738  riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
83739  pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
83740  bs_cache <<= riceParamPartLoBitCount;
83741  }
83742  } else {
83743  ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
83744  for (;;) {
83745  if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
83746  #ifndef MA_DR_FLAC_NO_CRC
83747  ma_dr_flac__update_crc16(bs);
83748  #endif
83749  bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83750  bs_consumedBits = 0;
83751  #ifndef MA_DR_FLAC_NO_CRC
83752  bs->crc16Cache = bs_cache;
83753  #endif
83754  } else {
83755  if (!ma_dr_flac__reload_cache(bs)) {
83756  return MA_FALSE;
83757  }
83758  bs_cache = bs->cache;
83759  bs_consumedBits = bs->consumedBits;
83760  }
83761  lzcount = ma_dr_flac__clz(bs_cache);
83762  zeroCounter += lzcount;
83763  if (lzcount < sizeof(bs_cache)*8) {
83764  break;
83765  }
83766  }
83767  pZeroCounterOut[0] = zeroCounter;
83768  goto extract_rice_param_part;
83769  }
83770  bs->cache = bs_cache;
83771  bs->consumedBits = bs_consumedBits;
83772  return MA_TRUE;
83773 }
83774 static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam)
83775 {
83776  ma_uint32 riceParamPlus1 = riceParam + 1;
83777  ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
83778  ma_dr_flac_cache_t bs_cache = bs->cache;
83779  ma_uint32 bs_consumedBits = bs->consumedBits;
83780  ma_uint32 lzcount = ma_dr_flac__clz(bs_cache);
83781  if (lzcount < sizeof(bs_cache)*8) {
83782  extract_rice_param_part:
83783  bs_cache <<= lzcount;
83784  bs_consumedBits += lzcount;
83785  if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
83786  bs_cache <<= riceParamPlus1;
83787  bs_consumedBits += riceParamPlus1;
83788  } else {
83789  ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
83790  MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
83791  if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
83792  #ifndef MA_DR_FLAC_NO_CRC
83793  ma_dr_flac__update_crc16(bs);
83794  #endif
83795  bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83796  bs_consumedBits = riceParamPartLoBitCount;
83797  #ifndef MA_DR_FLAC_NO_CRC
83798  bs->crc16Cache = bs_cache;
83799  #endif
83800  } else {
83801  if (!ma_dr_flac__reload_cache(bs)) {
83802  return MA_FALSE;
83803  }
83804  if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
83805  return MA_FALSE;
83806  }
83807  bs_cache = bs->cache;
83808  bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
83809  }
83810  bs_cache <<= riceParamPartLoBitCount;
83811  }
83812  } else {
83813  for (;;) {
83814  if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
83815  #ifndef MA_DR_FLAC_NO_CRC
83816  ma_dr_flac__update_crc16(bs);
83817  #endif
83818  bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83819  bs_consumedBits = 0;
83820  #ifndef MA_DR_FLAC_NO_CRC
83821  bs->crc16Cache = bs_cache;
83822  #endif
83823  } else {
83824  if (!ma_dr_flac__reload_cache(bs)) {
83825  return MA_FALSE;
83826  }
83827  bs_cache = bs->cache;
83828  bs_consumedBits = bs->consumedBits;
83829  }
83830  lzcount = ma_dr_flac__clz(bs_cache);
83831  if (lzcount < sizeof(bs_cache)*8) {
83832  break;
83833  }
83834  }
83835  goto extract_rice_param_part;
83836  }
83837  bs->cache = bs_cache;
83838  bs->consumedBits = bs_consumedBits;
83839  return MA_TRUE;
83840 }
83841 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)
83842 {
83843  ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
83844  ma_uint32 zeroCountPart0;
83845  ma_uint32 riceParamPart0;
83846  ma_uint32 riceParamMask;
83847  ma_uint32 i;
83848  MA_DR_FLAC_ASSERT(bs != NULL);
83849  MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
83850  (void)bitsPerSample;
83851  (void)order;
83852  (void)shift;
83853  (void)coefficients;
83854  riceParamMask = (ma_uint32)~((~0UL) << riceParam);
83855  i = 0;
83856  while (i < count) {
83857  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
83858  return MA_FALSE;
83859  }
83860  riceParamPart0 &= riceParamMask;
83861  riceParamPart0 |= (zeroCountPart0 << riceParam);
83862  riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
83863  pSamplesOut[i] = riceParamPart0;
83864  i += 1;
83865  }
83866  return MA_TRUE;
83867 }
83868 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)
83869 {
83870  ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
83871  ma_uint32 zeroCountPart0 = 0;
83872  ma_uint32 zeroCountPart1 = 0;
83873  ma_uint32 zeroCountPart2 = 0;
83874  ma_uint32 zeroCountPart3 = 0;
83875  ma_uint32 riceParamPart0 = 0;
83876  ma_uint32 riceParamPart1 = 0;
83877  ma_uint32 riceParamPart2 = 0;
83878  ma_uint32 riceParamPart3 = 0;
83879  ma_uint32 riceParamMask;
83880  const ma_int32* pSamplesOutEnd;
83881  ma_uint32 i;
83882  MA_DR_FLAC_ASSERT(bs != NULL);
83883  MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
83884  if (lpcOrder == 0) {
83885  return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
83886  }
83887  riceParamMask = (ma_uint32)~((~0UL) << riceParam);
83888  pSamplesOutEnd = pSamplesOut + (count & ~3);
83889  if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
83890  while (pSamplesOut < pSamplesOutEnd) {
83891  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
83892  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
83893  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
83894  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
83895  return MA_FALSE;
83896  }
83897  riceParamPart0 &= riceParamMask;
83898  riceParamPart1 &= riceParamMask;
83899  riceParamPart2 &= riceParamMask;
83900  riceParamPart3 &= riceParamMask;
83901  riceParamPart0 |= (zeroCountPart0 << riceParam);
83902  riceParamPart1 |= (zeroCountPart1 << riceParam);
83903  riceParamPart2 |= (zeroCountPart2 << riceParam);
83904  riceParamPart3 |= (zeroCountPart3 << riceParam);
83905  riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
83906  riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
83907  riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
83908  riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
83909  pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
83910  pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
83911  pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
83912  pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
83913  pSamplesOut += 4;
83914  }
83915  } else {
83916  while (pSamplesOut < pSamplesOutEnd) {
83917  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
83918  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
83919  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
83920  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
83921  return MA_FALSE;
83922  }
83923  riceParamPart0 &= riceParamMask;
83924  riceParamPart1 &= riceParamMask;
83925  riceParamPart2 &= riceParamMask;
83926  riceParamPart3 &= riceParamMask;
83927  riceParamPart0 |= (zeroCountPart0 << riceParam);
83928  riceParamPart1 |= (zeroCountPart1 << riceParam);
83929  riceParamPart2 |= (zeroCountPart2 << riceParam);
83930  riceParamPart3 |= (zeroCountPart3 << riceParam);
83931  riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
83932  riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
83933  riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
83934  riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
83935  pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
83936  pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
83937  pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
83938  pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
83939  pSamplesOut += 4;
83940  }
83941  }
83942  i = (count & ~3);
83943  while (i < count) {
83944  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
83945  return MA_FALSE;
83946  }
83947  riceParamPart0 &= riceParamMask;
83948  riceParamPart0 |= (zeroCountPart0 << riceParam);
83949  riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
83950  if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
83951  pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
83952  } else {
83953  pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
83954  }
83955  i += 1;
83956  pSamplesOut += 1;
83957  }
83958  return MA_TRUE;
83959 }
83960 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
83961 static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
83962 {
83963  __m128i r;
83964  r = _mm_packs_epi32(a, b);
83965  r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));
83966  r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
83967  r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
83968  return r;
83969 }
83970 #endif
83971 #if defined(MA_DR_FLAC_SUPPORT_SSE41)
83972 static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a)
83973 {
83974  return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
83975 }
83976 static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x)
83977 {
83978  __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
83979  __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));
83980  return _mm_add_epi32(x64, x32);
83981 }
83982 static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x)
83983 {
83984  return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
83985 }
83986 static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count)
83987 {
83988  __m128i lo = _mm_srli_epi64(x, count);
83989  __m128i hi = _mm_srai_epi32(x, count);
83990  hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0));
83991  return _mm_or_si128(lo, hi);
83992 }
83993 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)
83994 {
83995  int i;
83996  ma_uint32 riceParamMask;
83997  ma_int32* pDecodedSamples = pSamplesOut;
83998  ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
83999  ma_uint32 zeroCountParts0 = 0;
84000  ma_uint32 zeroCountParts1 = 0;
84001  ma_uint32 zeroCountParts2 = 0;
84002  ma_uint32 zeroCountParts3 = 0;
84003  ma_uint32 riceParamParts0 = 0;
84004  ma_uint32 riceParamParts1 = 0;
84005  ma_uint32 riceParamParts2 = 0;
84006  ma_uint32 riceParamParts3 = 0;
84007  __m128i coefficients128_0;
84008  __m128i coefficients128_4;
84009  __m128i coefficients128_8;
84010  __m128i samples128_0;
84011  __m128i samples128_4;
84012  __m128i samples128_8;
84013  __m128i riceParamMask128;
84014  const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
84015  riceParamMask = (ma_uint32)~((~0UL) << riceParam);
84016  riceParamMask128 = _mm_set1_epi32(riceParamMask);
84017  coefficients128_0 = _mm_setzero_si128();
84018  coefficients128_4 = _mm_setzero_si128();
84019  coefficients128_8 = _mm_setzero_si128();
84020  samples128_0 = _mm_setzero_si128();
84021  samples128_4 = _mm_setzero_si128();
84022  samples128_8 = _mm_setzero_si128();
84023 #if 1
84024  {
84025  int runningOrder = order;
84026  if (runningOrder >= 4) {
84027  coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
84028  samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
84029  runningOrder -= 4;
84030  } else {
84031  switch (runningOrder) {
84032  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;
84033  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;
84034  case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
84035  }
84036  runningOrder = 0;
84037  }
84038  if (runningOrder >= 4) {
84039  coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
84040  samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
84041  runningOrder -= 4;
84042  } else {
84043  switch (runningOrder) {
84044  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;
84045  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;
84046  case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
84047  }
84048  runningOrder = 0;
84049  }
84050  if (runningOrder == 4) {
84051  coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
84052  samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
84053  runningOrder -= 4;
84054  } else {
84055  switch (runningOrder) {
84056  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;
84057  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;
84058  case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
84059  }
84060  runningOrder = 0;
84061  }
84062  coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
84063  coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
84064  coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
84065  }
84066 #else
84067  switch (order)
84068  {
84069  case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12];
84070  case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11];
84071  case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10];
84072  case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
84073  case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
84074  case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
84075  case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
84076  case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
84077  case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
84078  case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
84079  case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
84080  case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
84081  }
84082 #endif
84083  while (pDecodedSamples < pDecodedSamplesEnd) {
84084  __m128i prediction128;
84085  __m128i zeroCountPart128;
84086  __m128i riceParamPart128;
84087  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
84088  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
84089  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
84090  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
84091  return MA_FALSE;
84092  }
84093  zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
84094  riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
84095  riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
84096  riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
84097  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)));
84098  if (order <= 4) {
84099  for (i = 0; i < 4; i += 1) {
84100  prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);
84101  prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);
84102  prediction128 = _mm_srai_epi32(prediction128, shift);
84103  prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
84104  samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
84105  riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
84106  }
84107  } else if (order <= 8) {
84108  for (i = 0; i < 4; i += 1) {
84109  prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4);
84110  prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
84111  prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);
84112  prediction128 = _mm_srai_epi32(prediction128, shift);
84113  prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
84114  samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
84115  samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
84116  riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
84117  }
84118  } else {
84119  for (i = 0; i < 4; i += 1) {
84120  prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8);
84121  prediction128 = _mm_add_epi32(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_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
84127  samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
84128  samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
84129  riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
84130  }
84131  }
84132  _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
84133  pDecodedSamples += 4;
84134  }
84135  i = (count & ~3);
84136  while (i < (int)count) {
84137  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
84138  return MA_FALSE;
84139  }
84140  riceParamParts0 &= riceParamMask;
84141  riceParamParts0 |= (zeroCountParts0 << riceParam);
84142  riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
84143  pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
84144  i += 1;
84145  pDecodedSamples += 1;
84146  }
84147  return MA_TRUE;
84148 }
84149 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)
84150 {
84151  int i;
84152  ma_uint32 riceParamMask;
84153  ma_int32* pDecodedSamples = pSamplesOut;
84154  ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
84155  ma_uint32 zeroCountParts0 = 0;
84156  ma_uint32 zeroCountParts1 = 0;
84157  ma_uint32 zeroCountParts2 = 0;
84158  ma_uint32 zeroCountParts3 = 0;
84159  ma_uint32 riceParamParts0 = 0;
84160  ma_uint32 riceParamParts1 = 0;
84161  ma_uint32 riceParamParts2 = 0;
84162  ma_uint32 riceParamParts3 = 0;
84163  __m128i coefficients128_0;
84164  __m128i coefficients128_4;
84165  __m128i coefficients128_8;
84166  __m128i samples128_0;
84167  __m128i samples128_4;
84168  __m128i samples128_8;
84169  __m128i prediction128;
84170  __m128i riceParamMask128;
84171  const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
84172  MA_DR_FLAC_ASSERT(order <= 12);
84173  riceParamMask = (ma_uint32)~((~0UL) << riceParam);
84174  riceParamMask128 = _mm_set1_epi32(riceParamMask);
84175  prediction128 = _mm_setzero_si128();
84176  coefficients128_0 = _mm_setzero_si128();
84177  coefficients128_4 = _mm_setzero_si128();
84178  coefficients128_8 = _mm_setzero_si128();
84179  samples128_0 = _mm_setzero_si128();
84180  samples128_4 = _mm_setzero_si128();
84181  samples128_8 = _mm_setzero_si128();
84182 #if 1
84183  {
84184  int runningOrder = order;
84185  if (runningOrder >= 4) {
84186  coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
84187  samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
84188  runningOrder -= 4;
84189  } else {
84190  switch (runningOrder) {
84191  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;
84192  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;
84193  case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
84194  }
84195  runningOrder = 0;
84196  }
84197  if (runningOrder >= 4) {
84198  coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
84199  samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
84200  runningOrder -= 4;
84201  } else {
84202  switch (runningOrder) {
84203  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;
84204  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;
84205  case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
84206  }
84207  runningOrder = 0;
84208  }
84209  if (runningOrder == 4) {
84210  coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
84211  samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
84212  runningOrder -= 4;
84213  } else {
84214  switch (runningOrder) {
84215  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;
84216  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;
84217  case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
84218  }
84219  runningOrder = 0;
84220  }
84221  coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
84222  coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
84223  coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
84224  }
84225 #else
84226  switch (order)
84227  {
84228  case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12];
84229  case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11];
84230  case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10];
84231  case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
84232  case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
84233  case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
84234  case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
84235  case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
84236  case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
84237  case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
84238  case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
84239  case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
84240  }
84241 #endif
84242  while (pDecodedSamples < pDecodedSamplesEnd) {
84243  __m128i zeroCountPart128;
84244  __m128i riceParamPart128;
84245  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
84246  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
84247  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
84248  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
84249  return MA_FALSE;
84250  }
84251  zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
84252  riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
84253  riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
84254  riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
84255  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)));
84256  for (i = 0; i < 4; i += 1) {
84257  prediction128 = _mm_xor_si128(prediction128, prediction128);
84258  switch (order)
84259  {
84260  case 12:
84261  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))));
84262  case 10:
84263  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))));
84264  case 8:
84265  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))));
84266  case 6:
84267  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))));
84268  case 4:
84269  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))));
84270  case 2:
84271  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))));
84272  }
84273  prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128);
84274  prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift);
84275  prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
84276  samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
84277  samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
84278  samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
84279  riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
84280  }
84281  _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
84282  pDecodedSamples += 4;
84283  }
84284  i = (count & ~3);
84285  while (i < (int)count) {
84286  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
84287  return MA_FALSE;
84288  }
84289  riceParamParts0 &= riceParamMask;
84290  riceParamParts0 |= (zeroCountParts0 << riceParam);
84291  riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
84292  pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
84293  i += 1;
84294  pDecodedSamples += 1;
84295  }
84296  return MA_TRUE;
84297 }
84298 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)
84299 {
84300  MA_DR_FLAC_ASSERT(bs != NULL);
84301  MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
84302  if (lpcOrder > 0 && lpcOrder <= 12) {
84303  if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
84304  return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
84305  } else {
84306  return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
84307  }
84308  } else {
84309  return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84310  }
84311 }
84312 #endif
84313 #if defined(MA_DR_FLAC_SUPPORT_NEON)
84314 static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x)
84315 {
84316  vst1q_s32(p+0, x.val[0]);
84317  vst1q_s32(p+4, x.val[1]);
84318 }
84319 static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x)
84320 {
84321  vst1q_u32(p+0, x.val[0]);
84322  vst1q_u32(p+4, x.val[1]);
84323 }
84324 static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x)
84325 {
84326  vst1q_f32(p+0, x.val[0]);
84327  vst1q_f32(p+4, x.val[1]);
84328 }
84329 static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x)
84330 {
84331  vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));
84332 }
84333 static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x)
84334 {
84335  vst1q_u16(p, vcombine_u16(x.val[0], x.val[1]));
84336 }
84337 static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0)
84338 {
84339  ma_int32 x[4];
84340  x[3] = x3;
84341  x[2] = x2;
84342  x[1] = x1;
84343  x[0] = x0;
84344  return vld1q_s32(x);
84345 }
84346 static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b)
84347 {
84348  return vextq_s32(b, a, 1);
84349 }
84350 static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)
84351 {
84352  return vextq_u32(b, a, 1);
84353 }
84354 static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x)
84355 {
84356  int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));
84357  return vpadd_s32(r, r);
84358 }
84359 static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x)
84360 {
84361  return vadd_s64(vget_high_s64(x), vget_low_s64(x));
84362 }
84363 static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x)
84364 {
84365  return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));
84366 }
84367 static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x)
84368 {
84369  return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));
84370 }
84371 static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x)
84372 {
84373  return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));
84374 }
84375 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)
84376 {
84377  int i;
84378  ma_uint32 riceParamMask;
84379  ma_int32* pDecodedSamples = pSamplesOut;
84380  ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
84381  ma_uint32 zeroCountParts[4];
84382  ma_uint32 riceParamParts[4];
84383  int32x4_t coefficients128_0;
84384  int32x4_t coefficients128_4;
84385  int32x4_t coefficients128_8;
84386  int32x4_t samples128_0;
84387  int32x4_t samples128_4;
84388  int32x4_t samples128_8;
84389  uint32x4_t riceParamMask128;
84390  int32x4_t riceParam128;
84391  int32x2_t shift64;
84392  uint32x4_t one128;
84393  const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
84394  riceParamMask = (ma_uint32)~((~0UL) << riceParam);
84395  riceParamMask128 = vdupq_n_u32(riceParamMask);
84396  riceParam128 = vdupq_n_s32(riceParam);
84397  shift64 = vdup_n_s32(-shift);
84398  one128 = vdupq_n_u32(1);
84399  {
84400  int runningOrder = order;
84401  ma_int32 tempC[4] = {0, 0, 0, 0};
84402  ma_int32 tempS[4] = {0, 0, 0, 0};
84403  if (runningOrder >= 4) {
84404  coefficients128_0 = vld1q_s32(coefficients + 0);
84405  samples128_0 = vld1q_s32(pSamplesOut - 4);
84406  runningOrder -= 4;
84407  } else {
84408  switch (runningOrder) {
84409  case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
84410  case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
84411  case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
84412  }
84413  coefficients128_0 = vld1q_s32(tempC);
84414  samples128_0 = vld1q_s32(tempS);
84415  runningOrder = 0;
84416  }
84417  if (runningOrder >= 4) {
84418  coefficients128_4 = vld1q_s32(coefficients + 4);
84419  samples128_4 = vld1q_s32(pSamplesOut - 8);
84420  runningOrder -= 4;
84421  } else {
84422  switch (runningOrder) {
84423  case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
84424  case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
84425  case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
84426  }
84427  coefficients128_4 = vld1q_s32(tempC);
84428  samples128_4 = vld1q_s32(tempS);
84429  runningOrder = 0;
84430  }
84431  if (runningOrder == 4) {
84432  coefficients128_8 = vld1q_s32(coefficients + 8);
84433  samples128_8 = vld1q_s32(pSamplesOut - 12);
84434  runningOrder -= 4;
84435  } else {
84436  switch (runningOrder) {
84437  case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
84438  case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
84439  case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
84440  }
84441  coefficients128_8 = vld1q_s32(tempC);
84442  samples128_8 = vld1q_s32(tempS);
84443  runningOrder = 0;
84444  }
84445  coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0);
84446  coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4);
84447  coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8);
84448  }
84449  while (pDecodedSamples < pDecodedSamplesEnd) {
84450  int32x4_t prediction128;
84451  int32x2_t prediction64;
84452  uint32x4_t zeroCountPart128;
84453  uint32x4_t riceParamPart128;
84454  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
84455  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
84456  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
84457  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
84458  return MA_FALSE;
84459  }
84460  zeroCountPart128 = vld1q_u32(zeroCountParts);
84461  riceParamPart128 = vld1q_u32(riceParamParts);
84462  riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
84463  riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
84464  riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
84465  if (order <= 4) {
84466  for (i = 0; i < 4; i += 1) {
84467  prediction128 = vmulq_s32(coefficients128_0, samples128_0);
84468  prediction64 = ma_dr_flac__vhaddq_s32(prediction128);
84469  prediction64 = vshl_s32(prediction64, shift64);
84470  prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
84471  samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
84472  riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
84473  }
84474  } else if (order <= 8) {
84475  for (i = 0; i < 4; i += 1) {
84476  prediction128 = vmulq_s32(coefficients128_4, samples128_4);
84477  prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
84478  prediction64 = ma_dr_flac__vhaddq_s32(prediction128);
84479  prediction64 = vshl_s32(prediction64, shift64);
84480  prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
84481  samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
84482  samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
84483  riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
84484  }
84485  } else {
84486  for (i = 0; i < 4; i += 1) {
84487  prediction128 = vmulq_s32(coefficients128_8, samples128_8);
84488  prediction128 = vmlaq_s32(prediction128, 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_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8);
84494  samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
84495  samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
84496  riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
84497  }
84498  }
84499  vst1q_s32(pDecodedSamples, samples128_0);
84500  pDecodedSamples += 4;
84501  }
84502  i = (count & ~3);
84503  while (i < (int)count) {
84504  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
84505  return MA_FALSE;
84506  }
84507  riceParamParts[0] &= riceParamMask;
84508  riceParamParts[0] |= (zeroCountParts[0] << riceParam);
84509  riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
84510  pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
84511  i += 1;
84512  pDecodedSamples += 1;
84513  }
84514  return MA_TRUE;
84515 }
84516 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)
84517 {
84518  int i;
84519  ma_uint32 riceParamMask;
84520  ma_int32* pDecodedSamples = pSamplesOut;
84521  ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
84522  ma_uint32 zeroCountParts[4];
84523  ma_uint32 riceParamParts[4];
84524  int32x4_t coefficients128_0;
84525  int32x4_t coefficients128_4;
84526  int32x4_t coefficients128_8;
84527  int32x4_t samples128_0;
84528  int32x4_t samples128_4;
84529  int32x4_t samples128_8;
84530  uint32x4_t riceParamMask128;
84531  int32x4_t riceParam128;
84532  int64x1_t shift64;
84533  uint32x4_t one128;
84534  int64x2_t prediction128 = { 0 };
84535  uint32x4_t zeroCountPart128;
84536  uint32x4_t riceParamPart128;
84537  const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
84538  riceParamMask = (ma_uint32)~((~0UL) << riceParam);
84539  riceParamMask128 = vdupq_n_u32(riceParamMask);
84540  riceParam128 = vdupq_n_s32(riceParam);
84541  shift64 = vdup_n_s64(-shift);
84542  one128 = vdupq_n_u32(1);
84543  {
84544  int runningOrder = order;
84545  ma_int32 tempC[4] = {0, 0, 0, 0};
84546  ma_int32 tempS[4] = {0, 0, 0, 0};
84547  if (runningOrder >= 4) {
84548  coefficients128_0 = vld1q_s32(coefficients + 0);
84549  samples128_0 = vld1q_s32(pSamplesOut - 4);
84550  runningOrder -= 4;
84551  } else {
84552  switch (runningOrder) {
84553  case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
84554  case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
84555  case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
84556  }
84557  coefficients128_0 = vld1q_s32(tempC);
84558  samples128_0 = vld1q_s32(tempS);
84559  runningOrder = 0;
84560  }
84561  if (runningOrder >= 4) {
84562  coefficients128_4 = vld1q_s32(coefficients + 4);
84563  samples128_4 = vld1q_s32(pSamplesOut - 8);
84564  runningOrder -= 4;
84565  } else {
84566  switch (runningOrder) {
84567  case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
84568  case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
84569  case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
84570  }
84571  coefficients128_4 = vld1q_s32(tempC);
84572  samples128_4 = vld1q_s32(tempS);
84573  runningOrder = 0;
84574  }
84575  if (runningOrder == 4) {
84576  coefficients128_8 = vld1q_s32(coefficients + 8);
84577  samples128_8 = vld1q_s32(pSamplesOut - 12);
84578  runningOrder -= 4;
84579  } else {
84580  switch (runningOrder) {
84581  case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
84582  case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
84583  case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
84584  }
84585  coefficients128_8 = vld1q_s32(tempC);
84586  samples128_8 = vld1q_s32(tempS);
84587  runningOrder = 0;
84588  }
84589  coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0);
84590  coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4);
84591  coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8);
84592  }
84593  while (pDecodedSamples < pDecodedSamplesEnd) {
84594  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
84595  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
84596  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
84597  !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
84598  return MA_FALSE;
84599  }
84600  zeroCountPart128 = vld1q_u32(zeroCountParts);
84601  riceParamPart128 = vld1q_u32(riceParamParts);
84602  riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
84603  riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
84604  riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
84605  for (i = 0; i < 4; i += 1) {
84606  int64x1_t prediction64;
84607  prediction128 = veorq_s64(prediction128, prediction128);
84608  switch (order)
84609  {
84610  case 12:
84611  case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));
84612  case 10:
84613  case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));
84614  case 8:
84615  case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));
84616  case 6:
84617  case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));
84618  case 4:
84619  case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));
84620  case 2:
84621  case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));
84622  }
84623  prediction64 = ma_dr_flac__vhaddq_s64(prediction128);
84624  prediction64 = vshl_s64(prediction64, shift64);
84625  prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));
84626  samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8);
84627  samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
84628  samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);
84629  riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
84630  }
84631  vst1q_s32(pDecodedSamples, samples128_0);
84632  pDecodedSamples += 4;
84633  }
84634  i = (count & ~3);
84635  while (i < (int)count) {
84636  if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
84637  return MA_FALSE;
84638  }
84639  riceParamParts[0] &= riceParamMask;
84640  riceParamParts[0] |= (zeroCountParts[0] << riceParam);
84641  riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
84642  pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
84643  i += 1;
84644  pDecodedSamples += 1;
84645  }
84646  return MA_TRUE;
84647 }
84648 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)
84649 {
84650  MA_DR_FLAC_ASSERT(bs != NULL);
84651  MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
84652  if (lpcOrder > 0 && lpcOrder <= 12) {
84653  if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
84654  return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
84655  } else {
84656  return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
84657  }
84658  } else {
84659  return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84660  }
84661 }
84662 #endif
84663 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)
84664 {
84665 #if defined(MA_DR_FLAC_SUPPORT_SSE41)
84666  if (ma_dr_flac__gIsSSE41Supported) {
84667  return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84668  } else
84669 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
84670  if (ma_dr_flac__gIsNEONSupported) {
84671  return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84672  } else
84673 #endif
84674  {
84675  #if 0
84676  return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84677  #else
84678  return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84679  #endif
84680  }
84681 }
84682 static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam)
84683 {
84684  ma_uint32 i;
84685  MA_DR_FLAC_ASSERT(bs != NULL);
84686  for (i = 0; i < count; ++i) {
84687  if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) {
84688  return MA_FALSE;
84689  }
84690  }
84691  return MA_TRUE;
84692 }
84693 #if defined(__clang__)
84694 __attribute__((no_sanitize("signed-integer-overflow")))
84695 #endif
84696 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)
84697 {
84698  ma_uint32 i;
84699  MA_DR_FLAC_ASSERT(bs != NULL);
84700  MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31);
84701  MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
84702  for (i = 0; i < count; ++i) {
84703  if (unencodedBitsPerSample > 0) {
84704  if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
84705  return MA_FALSE;
84706  }
84707  } else {
84708  pSamplesOut[i] = 0;
84709  }
84710  if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
84711  pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
84712  } else {
84713  pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
84714  }
84715  }
84716  return MA_TRUE;
84717 }
84718 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)
84719 {
84720  ma_uint8 residualMethod;
84721  ma_uint8 partitionOrder;
84722  ma_uint32 samplesInPartition;
84723  ma_uint32 partitionsRemaining;
84724  MA_DR_FLAC_ASSERT(bs != NULL);
84725  MA_DR_FLAC_ASSERT(blockSize != 0);
84726  MA_DR_FLAC_ASSERT(pDecodedSamples != NULL);
84727  if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) {
84728  return MA_FALSE;
84729  }
84730  if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
84731  return MA_FALSE;
84732  }
84733  pDecodedSamples += lpcOrder;
84734  if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) {
84735  return MA_FALSE;
84736  }
84737  if (partitionOrder > 8) {
84738  return MA_FALSE;
84739  }
84740  if ((blockSize / (1 << partitionOrder)) < lpcOrder) {
84741  return MA_FALSE;
84742  }
84743  samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder;
84744  partitionsRemaining = (1 << partitionOrder);
84745  for (;;) {
84746  ma_uint8 riceParam = 0;
84747  if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
84748  if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) {
84749  return MA_FALSE;
84750  }
84751  if (riceParam == 15) {
84752  riceParam = 0xFF;
84753  }
84754  } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
84755  if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) {
84756  return MA_FALSE;
84757  }
84758  if (riceParam == 31) {
84759  riceParam = 0xFF;
84760  }
84761  }
84762  if (riceParam != 0xFF) {
84763  if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
84764  return MA_FALSE;
84765  }
84766  } else {
84767  ma_uint8 unencodedBitsPerSample = 0;
84768  if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
84769  return MA_FALSE;
84770  }
84771  if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
84772  return MA_FALSE;
84773  }
84774  }
84775  pDecodedSamples += samplesInPartition;
84776  if (partitionsRemaining == 1) {
84777  break;
84778  }
84779  partitionsRemaining -= 1;
84780  if (partitionOrder != 0) {
84781  samplesInPartition = blockSize / (1 << partitionOrder);
84782  }
84783  }
84784  return MA_TRUE;
84785 }
84786 static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order)
84787 {
84788  ma_uint8 residualMethod;
84789  ma_uint8 partitionOrder;
84790  ma_uint32 samplesInPartition;
84791  ma_uint32 partitionsRemaining;
84792  MA_DR_FLAC_ASSERT(bs != NULL);
84793  MA_DR_FLAC_ASSERT(blockSize != 0);
84794  if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) {
84795  return MA_FALSE;
84796  }
84797  if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
84798  return MA_FALSE;
84799  }
84800  if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) {
84801  return MA_FALSE;
84802  }
84803  if (partitionOrder > 8) {
84804  return MA_FALSE;
84805  }
84806  if ((blockSize / (1 << partitionOrder)) <= order) {
84807  return MA_FALSE;
84808  }
84809  samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
84810  partitionsRemaining = (1 << partitionOrder);
84811  for (;;)
84812  {
84813  ma_uint8 riceParam = 0;
84814  if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
84815  if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) {
84816  return MA_FALSE;
84817  }
84818  if (riceParam == 15) {
84819  riceParam = 0xFF;
84820  }
84821  } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
84822  if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) {
84823  return MA_FALSE;
84824  }
84825  if (riceParam == 31) {
84826  riceParam = 0xFF;
84827  }
84828  }
84829  if (riceParam != 0xFF) {
84830  if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
84831  return MA_FALSE;
84832  }
84833  } else {
84834  ma_uint8 unencodedBitsPerSample = 0;
84835  if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
84836  return MA_FALSE;
84837  }
84838  if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
84839  return MA_FALSE;
84840  }
84841  }
84842  if (partitionsRemaining == 1) {
84843  break;
84844  }
84845  partitionsRemaining -= 1;
84846  samplesInPartition = blockSize / (1 << partitionOrder);
84847  }
84848  return MA_TRUE;
84849 }
84850 static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples)
84851 {
84852  ma_uint32 i;
84853  ma_int32 sample;
84854  if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
84855  return MA_FALSE;
84856  }
84857  for (i = 0; i < blockSize; ++i) {
84858  pDecodedSamples[i] = sample;
84859  }
84860  return MA_TRUE;
84861 }
84862 static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples)
84863 {
84864  ma_uint32 i;
84865  for (i = 0; i < blockSize; ++i) {
84866  ma_int32 sample;
84867  if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
84868  return MA_FALSE;
84869  }
84870  pDecodedSamples[i] = sample;
84871  }
84872  return MA_TRUE;
84873 }
84874 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)
84875 {
84876  ma_uint32 i;
84877  static ma_int32 lpcCoefficientsTable[5][4] = {
84878  {0, 0, 0, 0},
84879  {1, 0, 0, 0},
84880  {2, -1, 0, 0},
84881  {3, -3, 1, 0},
84882  {4, -6, 4, -1}
84883  };
84884  for (i = 0; i < lpcOrder; ++i) {
84885  ma_int32 sample;
84886  if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
84887  return MA_FALSE;
84888  }
84889  pDecodedSamples[i] = sample;
84890  }
84891  if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
84892  return MA_FALSE;
84893  }
84894  return MA_TRUE;
84895 }
84896 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)
84897 {
84898  ma_uint8 i;
84899  ma_uint8 lpcPrecision;
84900  ma_int8 lpcShift;
84901  ma_int32 coefficients[32];
84902  for (i = 0; i < lpcOrder; ++i) {
84903  ma_int32 sample;
84904  if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) {
84905  return MA_FALSE;
84906  }
84907  pDecodedSamples[i] = sample;
84908  }
84909  if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) {
84910  return MA_FALSE;
84911  }
84912  if (lpcPrecision == 15) {
84913  return MA_FALSE;
84914  }
84915  lpcPrecision += 1;
84916  if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) {
84917  return MA_FALSE;
84918  }
84919  if (lpcShift < 0) {
84920  return MA_FALSE;
84921  }
84922  MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
84923  for (i = 0; i < lpcOrder; ++i) {
84924  if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) {
84925  return MA_FALSE;
84926  }
84927  }
84928  if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
84929  return MA_FALSE;
84930  }
84931  return MA_TRUE;
84932 }
84933 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)
84934 {
84935  const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
84936  const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1};
84937  MA_DR_FLAC_ASSERT(bs != NULL);
84938  MA_DR_FLAC_ASSERT(header != NULL);
84939  for (;;) {
84940  ma_uint8 crc8 = 0xCE;
84941  ma_uint8 reserved = 0;
84942  ma_uint8 blockingStrategy = 0;
84943  ma_uint8 blockSize = 0;
84944  ma_uint8 sampleRate = 0;
84945  ma_uint8 channelAssignment = 0;
84946  ma_uint8 bitsPerSample = 0;
84947  ma_bool32 isVariableBlockSize;
84948  if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) {
84949  return MA_FALSE;
84950  }
84951  if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) {
84952  return MA_FALSE;
84953  }
84954  if (reserved == 1) {
84955  continue;
84956  }
84957  crc8 = ma_dr_flac_crc8(crc8, reserved, 1);
84958  if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) {
84959  return MA_FALSE;
84960  }
84961  crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1);
84962  if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) {
84963  return MA_FALSE;
84964  }
84965  if (blockSize == 0) {
84966  continue;
84967  }
84968  crc8 = ma_dr_flac_crc8(crc8, blockSize, 4);
84969  if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) {
84970  return MA_FALSE;
84971  }
84972  crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4);
84973  if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) {
84974  return MA_FALSE;
84975  }
84976  if (channelAssignment > 10) {
84977  continue;
84978  }
84979  crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4);
84980  if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) {
84981  return MA_FALSE;
84982  }
84983  if (bitsPerSample == 3 || bitsPerSample == 7) {
84984  continue;
84985  }
84986  crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3);
84987  if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) {
84988  return MA_FALSE;
84989  }
84990  if (reserved == 1) {
84991  continue;
84992  }
84993  crc8 = ma_dr_flac_crc8(crc8, reserved, 1);
84994  isVariableBlockSize = blockingStrategy == 1;
84995  if (isVariableBlockSize) {
84996  ma_uint64 pcmFrameNumber;
84997  ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
84998  if (result != MA_SUCCESS) {
84999  if (result == MA_AT_END) {
85000  return MA_FALSE;
85001  } else {
85002  continue;
85003  }
85004  }
85005  header->flacFrameNumber = 0;
85006  header->pcmFrameNumber = pcmFrameNumber;
85007  } else {
85008  ma_uint64 flacFrameNumber = 0;
85009  ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &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 = (ma_uint32)flacFrameNumber;
85018  header->pcmFrameNumber = 0;
85019  }
85020  MA_DR_FLAC_ASSERT(blockSize > 0);
85021  if (blockSize == 1) {
85022  header->blockSizeInPCMFrames = 192;
85023  } else if (blockSize <= 5) {
85024  MA_DR_FLAC_ASSERT(blockSize >= 2);
85025  header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));
85026  } else if (blockSize == 6) {
85027  if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {
85028  return MA_FALSE;
85029  }
85030  crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8);
85031  header->blockSizeInPCMFrames += 1;
85032  } else if (blockSize == 7) {
85033  if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {
85034  return MA_FALSE;
85035  }
85036  crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16);
85037  if (header->blockSizeInPCMFrames == 0xFFFF) {
85038  return MA_FALSE;
85039  }
85040  header->blockSizeInPCMFrames += 1;
85041  } else {
85042  MA_DR_FLAC_ASSERT(blockSize >= 8);
85043  header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8));
85044  }
85045  if (sampleRate <= 11) {
85046  header->sampleRate = sampleRateTable[sampleRate];
85047  } else if (sampleRate == 12) {
85048  if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) {
85049  return MA_FALSE;
85050  }
85051  crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8);
85052  header->sampleRate *= 1000;
85053  } else if (sampleRate == 13) {
85054  if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) {
85055  return MA_FALSE;
85056  }
85057  crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16);
85058  } else if (sampleRate == 14) {
85059  if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) {
85060  return MA_FALSE;
85061  }
85062  crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16);
85063  header->sampleRate *= 10;
85064  } else {
85065  continue;
85066  }
85067  header->channelAssignment = channelAssignment;
85068  header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
85069  if (header->bitsPerSample == 0) {
85070  header->bitsPerSample = streaminfoBitsPerSample;
85071  }
85072  if (header->bitsPerSample != streaminfoBitsPerSample) {
85073  return MA_FALSE;
85074  }
85075  if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) {
85076  return MA_FALSE;
85077  }
85078 #ifndef MA_DR_FLAC_NO_CRC
85079  if (header->crc8 != crc8) {
85080  continue;
85081  }
85082 #endif
85083  return MA_TRUE;
85084  }
85085 }
85086 static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe)
85087 {
85088  ma_uint8 header;
85089  int type;
85090  if (!ma_dr_flac__read_uint8(bs, 8, &header)) {
85091  return MA_FALSE;
85092  }
85093  if ((header & 0x80) != 0) {
85094  return MA_FALSE;
85095  }
85096  type = (header & 0x7E) >> 1;
85097  if (type == 0) {
85098  pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT;
85099  } else if (type == 1) {
85100  pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM;
85101  } else {
85102  if ((type & 0x20) != 0) {
85103  pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC;
85104  pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1;
85105  } else if ((type & 0x08) != 0) {
85106  pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED;
85107  pSubframe->lpcOrder = (ma_uint8)(type & 0x07);
85108  if (pSubframe->lpcOrder > 4) {
85109  pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED;
85110  pSubframe->lpcOrder = 0;
85111  }
85112  } else {
85113  pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED;
85114  }
85115  }
85116  if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) {
85117  return MA_FALSE;
85118  }
85119  pSubframe->wastedBitsPerSample = 0;
85120  if ((header & 0x01) == 1) {
85121  unsigned int wastedBitsPerSample;
85122  if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
85123  return MA_FALSE;
85124  }
85125  pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1;
85126  }
85127  return MA_TRUE;
85128 }
85129 static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut)
85130 {
85131  ma_dr_flac_subframe* pSubframe;
85132  ma_uint32 subframeBitsPerSample;
85133  MA_DR_FLAC_ASSERT(bs != NULL);
85134  MA_DR_FLAC_ASSERT(frame != NULL);
85135  pSubframe = frame->subframes + subframeIndex;
85136  if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) {
85137  return MA_FALSE;
85138  }
85139  subframeBitsPerSample = frame->header.bitsPerSample;
85140  if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
85141  subframeBitsPerSample += 1;
85142  } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
85143  subframeBitsPerSample += 1;
85144  }
85145  if (subframeBitsPerSample > 32) {
85146  return MA_FALSE;
85147  }
85148  if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
85149  return MA_FALSE;
85150  }
85151  subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
85152  pSubframe->pSamplesS32 = pDecodedSamplesOut;
85153  switch (pSubframe->subframeType)
85154  {
85155  case MA_DR_FLAC_SUBFRAME_CONSTANT:
85156  {
85157  ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
85158  } break;
85159  case MA_DR_FLAC_SUBFRAME_VERBATIM:
85160  {
85161  ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
85162  } break;
85163  case MA_DR_FLAC_SUBFRAME_FIXED:
85164  {
85165  ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
85166  } break;
85167  case MA_DR_FLAC_SUBFRAME_LPC:
85168  {
85169  ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
85170  } break;
85171  default: return MA_FALSE;
85172  }
85173  return MA_TRUE;
85174 }
85175 static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex)
85176 {
85177  ma_dr_flac_subframe* pSubframe;
85178  ma_uint32 subframeBitsPerSample;
85179  MA_DR_FLAC_ASSERT(bs != NULL);
85180  MA_DR_FLAC_ASSERT(frame != NULL);
85181  pSubframe = frame->subframes + subframeIndex;
85182  if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) {
85183  return MA_FALSE;
85184  }
85185  subframeBitsPerSample = frame->header.bitsPerSample;
85186  if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
85187  subframeBitsPerSample += 1;
85188  } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
85189  subframeBitsPerSample += 1;
85190  }
85191  if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
85192  return MA_FALSE;
85193  }
85194  subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
85195  pSubframe->pSamplesS32 = NULL;
85196  switch (pSubframe->subframeType)
85197  {
85198  case MA_DR_FLAC_SUBFRAME_CONSTANT:
85199  {
85200  if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) {
85201  return MA_FALSE;
85202  }
85203  } break;
85204  case MA_DR_FLAC_SUBFRAME_VERBATIM:
85205  {
85206  unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;
85207  if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
85208  return MA_FALSE;
85209  }
85210  } break;
85211  case MA_DR_FLAC_SUBFRAME_FIXED:
85212  {
85213  unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
85214  if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
85215  return MA_FALSE;
85216  }
85217  if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
85218  return MA_FALSE;
85219  }
85220  } break;
85221  case MA_DR_FLAC_SUBFRAME_LPC:
85222  {
85223  ma_uint8 lpcPrecision;
85224  unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
85225  if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
85226  return MA_FALSE;
85227  }
85228  if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) {
85229  return MA_FALSE;
85230  }
85231  if (lpcPrecision == 15) {
85232  return MA_FALSE;
85233  }
85234  lpcPrecision += 1;
85235  bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5;
85236  if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
85237  return MA_FALSE;
85238  }
85239  if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
85240  return MA_FALSE;
85241  }
85242  } break;
85243  default: return MA_FALSE;
85244  }
85245  return MA_TRUE;
85246 }
85247 static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment)
85248 {
85249  ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
85250  MA_DR_FLAC_ASSERT(channelAssignment <= 10);
85251  return lookup[channelAssignment];
85252 }
85253 static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac)
85254 {
85255  int channelCount;
85256  int i;
85257  ma_uint8 paddingSizeInBits;
85258  ma_uint16 desiredCRC16;
85259 #ifndef MA_DR_FLAC_NO_CRC
85260  ma_uint16 actualCRC16;
85261 #endif
85262  MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
85263  if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) {
85264  return MA_ERROR;
85265  }
85266  channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
85267  if (channelCount != (int)pFlac->channels) {
85268  return MA_ERROR;
85269  }
85270  for (i = 0; i < channelCount; ++i) {
85271  if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {
85272  return MA_ERROR;
85273  }
85274  }
85275  paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7);
85276  if (paddingSizeInBits > 0) {
85277  ma_uint8 padding = 0;
85278  if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
85279  return MA_AT_END;
85280  }
85281  }
85282 #ifndef MA_DR_FLAC_NO_CRC
85283  actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs);
85284 #endif
85285  if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
85286  return MA_AT_END;
85287  }
85288 #ifndef MA_DR_FLAC_NO_CRC
85289  if (actualCRC16 != desiredCRC16) {
85290  return MA_CRC_MISMATCH;
85291  }
85292 #endif
85293  pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
85294  return MA_SUCCESS;
85295 }
85296 static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac)
85297 {
85298  int channelCount;
85299  int i;
85300  ma_uint16 desiredCRC16;
85301 #ifndef MA_DR_FLAC_NO_CRC
85302  ma_uint16 actualCRC16;
85303 #endif
85304  channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
85305  for (i = 0; i < channelCount; ++i) {
85306  if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {
85307  return MA_ERROR;
85308  }
85309  }
85310  if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
85311  return MA_ERROR;
85312  }
85313 #ifndef MA_DR_FLAC_NO_CRC
85314  actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs);
85315 #endif
85316  if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
85317  return MA_AT_END;
85318  }
85319 #ifndef MA_DR_FLAC_NO_CRC
85320  if (actualCRC16 != desiredCRC16) {
85321  return MA_CRC_MISMATCH;
85322  }
85323 #endif
85324  return MA_SUCCESS;
85325 }
85326 static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac)
85327 {
85328  MA_DR_FLAC_ASSERT(pFlac != NULL);
85329  for (;;) {
85330  ma_result result;
85331  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85332  return MA_FALSE;
85333  }
85334  result = ma_dr_flac__decode_flac_frame(pFlac);
85335  if (result != MA_SUCCESS) {
85336  if (result == MA_CRC_MISMATCH) {
85337  continue;
85338  } else {
85339  return MA_FALSE;
85340  }
85341  }
85342  return MA_TRUE;
85343  }
85344 }
85345 static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame)
85346 {
85347  ma_uint64 firstPCMFrame;
85348  ma_uint64 lastPCMFrame;
85349  MA_DR_FLAC_ASSERT(pFlac != NULL);
85350  firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;
85351  if (firstPCMFrame == 0) {
85352  firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames;
85353  }
85354  lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
85355  if (lastPCMFrame > 0) {
85356  lastPCMFrame -= 1;
85357  }
85358  if (pFirstPCMFrame) {
85359  *pFirstPCMFrame = firstPCMFrame;
85360  }
85361  if (pLastPCMFrame) {
85362  *pLastPCMFrame = lastPCMFrame;
85363  }
85364 }
85365 static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac)
85366 {
85367  ma_bool32 result;
85368  MA_DR_FLAC_ASSERT(pFlac != NULL);
85369  result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);
85370  MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
85371  pFlac->currentPCMFrame = 0;
85372  return result;
85373 }
85374 static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac)
85375 {
85376  MA_DR_FLAC_ASSERT(pFlac != NULL);
85377  return ma_dr_flac__seek_flac_frame(pFlac);
85378 }
85379 static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek)
85380 {
85381  ma_uint64 pcmFramesRead = 0;
85382  while (pcmFramesToSeek > 0) {
85383  if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
85384  if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
85385  break;
85386  }
85387  } else {
85388  if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {
85389  pcmFramesRead += pcmFramesToSeek;
85390  pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek;
85391  pcmFramesToSeek = 0;
85392  } else {
85393  pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining;
85394  pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;
85395  pFlac->currentFLACFrame.pcmFramesRemaining = 0;
85396  }
85397  }
85398  }
85399  pFlac->currentPCMFrame += pcmFramesRead;
85400  return pcmFramesRead;
85401 }
85402 static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
85403 {
85404  ma_bool32 isMidFrame = MA_FALSE;
85405  ma_uint64 runningPCMFrameCount;
85406  MA_DR_FLAC_ASSERT(pFlac != NULL);
85407  if (pcmFrameIndex >= pFlac->currentPCMFrame) {
85408  runningPCMFrameCount = pFlac->currentPCMFrame;
85409  if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
85410  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85411  return MA_FALSE;
85412  }
85413  } else {
85414  isMidFrame = MA_TRUE;
85415  }
85416  } else {
85417  runningPCMFrameCount = 0;
85418  if (!ma_dr_flac__seek_to_first_frame(pFlac)) {
85419  return MA_FALSE;
85420  }
85421  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85422  return MA_FALSE;
85423  }
85424  }
85425  for (;;) {
85426  ma_uint64 pcmFrameCountInThisFLACFrame;
85427  ma_uint64 firstPCMFrameInFLACFrame = 0;
85428  ma_uint64 lastPCMFrameInFLACFrame = 0;
85429  ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
85430  pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
85431  if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
85432  ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
85433  if (!isMidFrame) {
85434  ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
85435  if (result == MA_SUCCESS) {
85436  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
85437  } else {
85438  if (result == MA_CRC_MISMATCH) {
85439  goto next_iteration;
85440  } else {
85441  return MA_FALSE;
85442  }
85443  }
85444  } else {
85445  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
85446  }
85447  } else {
85448  if (!isMidFrame) {
85449  ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
85450  if (result == MA_SUCCESS) {
85451  runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
85452  } else {
85453  if (result == MA_CRC_MISMATCH) {
85454  goto next_iteration;
85455  } else {
85456  return MA_FALSE;
85457  }
85458  }
85459  } else {
85460  runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
85461  pFlac->currentFLACFrame.pcmFramesRemaining = 0;
85462  isMidFrame = MA_FALSE;
85463  }
85464  if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
85465  return MA_TRUE;
85466  }
85467  }
85468  next_iteration:
85469  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85470  return MA_FALSE;
85471  }
85472  }
85473 }
85474 #if !defined(MA_DR_FLAC_NO_CRC)
85475 #define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f
85476 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)
85477 {
85478  MA_DR_FLAC_ASSERT(pFlac != NULL);
85479  MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);
85480  MA_DR_FLAC_ASSERT(targetByte >= rangeLo);
85481  MA_DR_FLAC_ASSERT(targetByte <= rangeHi);
85482  *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;
85483  for (;;) {
85484  ma_uint64 lastTargetByte = targetByte;
85485  if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) {
85486  if (targetByte == 0) {
85487  ma_dr_flac__seek_to_first_frame(pFlac);
85488  return MA_FALSE;
85489  }
85490  targetByte = rangeLo + ((rangeHi - rangeLo)/2);
85491  rangeHi = targetByte;
85492  } else {
85493  MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
85494 #if 1
85495  if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
85496  targetByte = rangeLo + ((rangeHi - rangeLo)/2);
85497  rangeHi = targetByte;
85498  } else {
85499  break;
85500  }
85501 #else
85502  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85503  targetByte = rangeLo + ((rangeHi - rangeLo)/2);
85504  rangeHi = targetByte;
85505  } else {
85506  break;
85507  }
85508 #endif
85509  }
85510  if(targetByte == lastTargetByte) {
85511  return MA_FALSE;
85512  }
85513  }
85514  ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
85515  MA_DR_FLAC_ASSERT(targetByte <= rangeHi);
85516  *pLastSuccessfulSeekOffset = targetByte;
85517  return MA_TRUE;
85518 }
85519 static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset)
85520 {
85521 #if 0
85522  if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) {
85523  if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) {
85524  return MA_FALSE;
85525  }
85526  }
85527 #endif
85528  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset;
85529 }
85530 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)
85531 {
85532  ma_uint64 targetByte;
85533  ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;
85534  ma_uint64 pcmRangeHi = 0;
85535  ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1;
85536  ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;
85537  ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
85538  targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);
85539  if (targetByte > byteRangeHi) {
85540  targetByte = byteRangeHi;
85541  }
85542  for (;;) {
85543  if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {
85544  ma_uint64 newPCMRangeLo;
85545  ma_uint64 newPCMRangeHi;
85546  ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);
85547  if (pcmRangeLo == newPCMRangeLo) {
85548  if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {
85549  break;
85550  }
85551  if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
85552  return MA_TRUE;
85553  } else {
85554  break;
85555  }
85556  }
85557  pcmRangeLo = newPCMRangeLo;
85558  pcmRangeHi = newPCMRangeHi;
85559  if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {
85560  if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {
85561  return MA_TRUE;
85562  } else {
85563  break;
85564  }
85565  } else {
85566  const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f);
85567  if (pcmRangeLo > pcmFrameIndex) {
85568  byteRangeHi = lastSuccessfulSeekOffset;
85569  if (byteRangeLo > byteRangeHi) {
85570  byteRangeLo = byteRangeHi;
85571  }
85572  targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);
85573  if (targetByte < byteRangeLo) {
85574  targetByte = byteRangeLo;
85575  }
85576  } else {
85577  if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {
85578  if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
85579  return MA_TRUE;
85580  } else {
85581  break;
85582  }
85583  } else {
85584  byteRangeLo = lastSuccessfulSeekOffset;
85585  if (byteRangeHi < byteRangeLo) {
85586  byteRangeHi = byteRangeLo;
85587  }
85588  targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio);
85589  if (targetByte > byteRangeHi) {
85590  targetByte = byteRangeHi;
85591  }
85592  if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {
85593  closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;
85594  }
85595  }
85596  }
85597  }
85598  } else {
85599  break;
85600  }
85601  }
85602  ma_dr_flac__seek_to_first_frame(pFlac);
85603  return MA_FALSE;
85604 }
85605 static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
85606 {
85607  ma_uint64 byteRangeLo;
85608  ma_uint64 byteRangeHi;
85609  ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
85610  if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) {
85611  return MA_FALSE;
85612  }
85613  if (pcmFrameIndex < seekForwardThreshold) {
85614  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;
85615  }
85616  byteRangeLo = pFlac->firstFLACFramePosInBytes;
85617  byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
85618  return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);
85619 }
85620 #endif
85621 static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
85622 {
85623  ma_uint32 iClosestSeekpoint = 0;
85624  ma_bool32 isMidFrame = MA_FALSE;
85625  ma_uint64 runningPCMFrameCount;
85626  ma_uint32 iSeekpoint;
85627  MA_DR_FLAC_ASSERT(pFlac != NULL);
85628  if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
85629  return MA_FALSE;
85630  }
85631  if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) {
85632  return MA_FALSE;
85633  }
85634  for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
85635  if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {
85636  break;
85637  }
85638  iClosestSeekpoint = iSeekpoint;
85639  }
85640  if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) {
85641  return MA_FALSE;
85642  }
85643  if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) {
85644  return MA_FALSE;
85645  }
85646 #if !defined(MA_DR_FLAC_NO_CRC)
85647  if (pFlac->totalPCMFrameCount > 0) {
85648  ma_uint64 byteRangeLo;
85649  ma_uint64 byteRangeHi;
85650  byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
85651  byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;
85652  if (iClosestSeekpoint < pFlac->seekpointCount-1) {
85653  ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1;
85654  if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) {
85655  return MA_FALSE;
85656  }
85657  if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) {
85658  byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1;
85659  }
85660  }
85661  if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
85662  if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85663  ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
85664  if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {
85665  return MA_TRUE;
85666  }
85667  }
85668  }
85669  }
85670 #endif
85671  if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {
85672  runningPCMFrameCount = pFlac->currentPCMFrame;
85673  if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
85674  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85675  return MA_FALSE;
85676  }
85677  } else {
85678  isMidFrame = MA_TRUE;
85679  }
85680  } else {
85681  runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;
85682  if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
85683  return MA_FALSE;
85684  }
85685  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85686  return MA_FALSE;
85687  }
85688  }
85689  for (;;) {
85690  ma_uint64 pcmFrameCountInThisFLACFrame;
85691  ma_uint64 firstPCMFrameInFLACFrame = 0;
85692  ma_uint64 lastPCMFrameInFLACFrame = 0;
85693  ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
85694  pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
85695  if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
85696  ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
85697  if (!isMidFrame) {
85698  ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
85699  if (result == MA_SUCCESS) {
85700  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
85701  } else {
85702  if (result == MA_CRC_MISMATCH) {
85703  goto next_iteration;
85704  } else {
85705  return MA_FALSE;
85706  }
85707  }
85708  } else {
85709  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
85710  }
85711  } else {
85712  if (!isMidFrame) {
85713  ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
85714  if (result == MA_SUCCESS) {
85715  runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
85716  } else {
85717  if (result == MA_CRC_MISMATCH) {
85718  goto next_iteration;
85719  } else {
85720  return MA_FALSE;
85721  }
85722  }
85723  } else {
85724  runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
85725  pFlac->currentFLACFrame.pcmFramesRemaining = 0;
85726  isMidFrame = MA_FALSE;
85727  }
85728  if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
85729  return MA_TRUE;
85730  }
85731  }
85732  next_iteration:
85733  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85734  return MA_FALSE;
85735  }
85736  }
85737 }
85738 #ifndef MA_DR_FLAC_NO_OGG
85739 typedef struct
85740 {
85741  ma_uint8 capturePattern[4];
85742  ma_uint8 structureVersion;
85743  ma_uint8 headerType;
85744  ma_uint64 granulePosition;
85745  ma_uint32 serialNumber;
85746  ma_uint32 sequenceNumber;
85747  ma_uint32 checksum;
85748  ma_uint8 segmentCount;
85749  ma_uint8 segmentTable[255];
85750 } ma_dr_flac_ogg_page_header;
85751 #endif
85752 typedef struct
85753 {
85754  ma_dr_flac_read_proc onRead;
85755  ma_dr_flac_seek_proc onSeek;
85756  ma_dr_flac_meta_proc onMeta;
85757  ma_dr_flac_container container;
85758  void* pUserData;
85759  void* pUserDataMD;
85760  ma_uint32 sampleRate;
85761  ma_uint8 channels;
85762  ma_uint8 bitsPerSample;
85763  ma_uint64 totalPCMFrameCount;
85764  ma_uint16 maxBlockSizeInPCMFrames;
85765  ma_uint64 runningFilePos;
85766  ma_bool32 hasStreamInfoBlock;
85767  ma_bool32 hasMetadataBlocks;
85768  ma_dr_flac_bs bs;
85769  ma_dr_flac_frame_header firstFrameHeader;
85770 #ifndef MA_DR_FLAC_NO_OGG
85771  ma_uint32 oggSerial;
85772  ma_uint64 oggFirstBytePos;
85773  ma_dr_flac_ogg_page_header oggBosHeader;
85774 #endif
85775 } ma_dr_flac_init_info;
85776 static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize)
85777 {
85778  blockHeader = ma_dr_flac__be2host_32(blockHeader);
85779  *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31);
85780  *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24);
85781  *blockSize = (blockHeader & 0x00FFFFFFUL);
85782 }
85783 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)
85784 {
85785  ma_uint32 blockHeader;
85786  *blockSize = 0;
85787  if (onRead(pUserData, &blockHeader, 4) != 4) {
85788  return MA_FALSE;
85789  }
85790  ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
85791  return MA_TRUE;
85792 }
85793 static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo)
85794 {
85795  ma_uint32 blockSizes;
85796  ma_uint64 frameSizes = 0;
85797  ma_uint64 importantProps;
85798  ma_uint8 md5[16];
85799  if (onRead(pUserData, &blockSizes, 4) != 4) {
85800  return MA_FALSE;
85801  }
85802  if (onRead(pUserData, &frameSizes, 6) != 6) {
85803  return MA_FALSE;
85804  }
85805  if (onRead(pUserData, &importantProps, 8) != 8) {
85806  return MA_FALSE;
85807  }
85808  if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
85809  return MA_FALSE;
85810  }
85811  blockSizes = ma_dr_flac__be2host_32(blockSizes);
85812  frameSizes = ma_dr_flac__be2host_64(frameSizes);
85813  importantProps = ma_dr_flac__be2host_64(importantProps);
85814  pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16);
85815  pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF);
85816  pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40);
85817  pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16);
85818  pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44);
85819  pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
85820  pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
85821  pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));
85822  MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));
85823  return MA_TRUE;
85824 }
85825 static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData)
85826 {
85827  (void)pUserData;
85828  return MA_DR_FLAC_MALLOC(sz);
85829 }
85830 static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData)
85831 {
85832  (void)pUserData;
85833  return MA_DR_FLAC_REALLOC(p, sz);
85834 }
85835 static void ma_dr_flac__free_default(void* p, void* pUserData)
85836 {
85837  (void)pUserData;
85838  MA_DR_FLAC_FREE(p);
85839 }
85840 static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
85841 {
85842  if (pAllocationCallbacks == NULL) {
85843  return NULL;
85844  }
85845  if (pAllocationCallbacks->onMalloc != NULL) {
85846  return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
85847  }
85848  if (pAllocationCallbacks->onRealloc != NULL) {
85849  return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
85850  }
85851  return NULL;
85852 }
85853 static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
85854 {
85855  if (pAllocationCallbacks == NULL) {
85856  return NULL;
85857  }
85858  if (pAllocationCallbacks->onRealloc != NULL) {
85859  return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
85860  }
85861  if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
85862  void* p2;
85863  p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
85864  if (p2 == NULL) {
85865  return NULL;
85866  }
85867  if (p != NULL) {
85868  MA_DR_FLAC_COPY_MEMORY(p2, p, szOld);
85869  pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
85870  }
85871  return p2;
85872  }
85873  return NULL;
85874 }
85875 static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
85876 {
85877  if (p == NULL || pAllocationCallbacks == NULL) {
85878  return;
85879  }
85880  if (pAllocationCallbacks->onFree != NULL) {
85881  pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
85882  }
85883 }
85884 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)
85885 {
85886  ma_uint64 runningFilePos = 42;
85887  ma_uint64 seektablePos = 0;
85888  ma_uint32 seektableSize = 0;
85889  for (;;) {
85890  ma_dr_flac_metadata metadata;
85891  ma_uint8 isLastBlock = 0;
85892  ma_uint8 blockType = 0;
85893  ma_uint32 blockSize;
85894  if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) {
85895  return MA_FALSE;
85896  }
85897  runningFilePos += 4;
85898  metadata.type = blockType;
85899  metadata.pRawData = NULL;
85900  metadata.rawDataSize = 0;
85901  switch (blockType)
85902  {
85903  case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION:
85904  {
85905  if (blockSize < 4) {
85906  return MA_FALSE;
85907  }
85908  if (onMeta) {
85909  void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
85910  if (pRawData == NULL) {
85911  return MA_FALSE;
85912  }
85913  if (onRead(pUserData, pRawData, blockSize) != blockSize) {
85914  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
85915  return MA_FALSE;
85916  }
85917  metadata.pRawData = pRawData;
85918  metadata.rawDataSize = blockSize;
85919  metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData);
85920  metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32));
85921  metadata.data.application.dataSize = blockSize - sizeof(ma_uint32);
85922  onMeta(pUserDataMD, &metadata);
85923  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
85924  }
85925  } break;
85926  case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
85927  {
85928  seektablePos = runningFilePos;
85929  seektableSize = blockSize;
85930  if (onMeta) {
85931  ma_uint32 seekpointCount;
85932  ma_uint32 iSeekpoint;
85933  void* pRawData;
85934  seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES;
85935  pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks);
85936  if (pRawData == NULL) {
85937  return MA_FALSE;
85938  }
85939  for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) {
85940  ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint;
85941  if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) {
85942  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
85943  return MA_FALSE;
85944  }
85945  pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame);
85946  pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset);
85947  pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount);
85948  }
85949  metadata.pRawData = pRawData;
85950  metadata.rawDataSize = blockSize;
85951  metadata.data.seektable.seekpointCount = seekpointCount;
85952  metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData;
85953  onMeta(pUserDataMD, &metadata);
85954  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
85955  }
85956  } break;
85957  case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
85958  {
85959  if (blockSize < 8) {
85960  return MA_FALSE;
85961  }
85962  if (onMeta) {
85963  void* pRawData;
85964  const char* pRunningData;
85965  const char* pRunningDataEnd;
85966  ma_uint32 i;
85967  pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
85968  if (pRawData == NULL) {
85969  return MA_FALSE;
85970  }
85971  if (onRead(pUserData, pRawData, blockSize) != blockSize) {
85972  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
85973  return MA_FALSE;
85974  }
85975  metadata.pRawData = pRawData;
85976  metadata.rawDataSize = blockSize;
85977  pRunningData = (const char*)pRawData;
85978  pRunningDataEnd = (const char*)pRawData + blockSize;
85979  metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85980  if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) {
85981  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
85982  return MA_FALSE;
85983  }
85984  metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
85985  metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85986  if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) {
85987  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
85988  return MA_FALSE;
85989  }
85990  metadata.data.vorbis_comment.pComments = pRunningData;
85991  for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
85992  ma_uint32 commentLength;
85993  if (pRunningDataEnd - pRunningData < 4) {
85994  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
85995  return MA_FALSE;
85996  }
85997  commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85998  if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) {
85999  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86000  return MA_FALSE;
86001  }
86002  pRunningData += commentLength;
86003  }
86004  onMeta(pUserDataMD, &metadata);
86005  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86006  }
86007  } break;
86008  case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET:
86009  {
86010  if (blockSize < 396) {
86011  return MA_FALSE;
86012  }
86013  if (onMeta) {
86014  void* pRawData;
86015  const char* pRunningData;
86016  const char* pRunningDataEnd;
86017  size_t bufferSize;
86018  ma_uint8 iTrack;
86019  ma_uint8 iIndex;
86020  void* pTrackData;
86021  pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
86022  if (pRawData == NULL) {
86023  return MA_FALSE;
86024  }
86025  if (onRead(pUserData, pRawData, blockSize) != blockSize) {
86026  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86027  return MA_FALSE;
86028  }
86029  metadata.pRawData = pRawData;
86030  metadata.rawDataSize = blockSize;
86031  pRunningData = (const char*)pRawData;
86032  pRunningDataEnd = (const char*)pRawData + blockSize;
86033  MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
86034  metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8;
86035  metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
86036  metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
86037  metadata.data.cuesheet.pTrackData = NULL;
86038  {
86039  const char* pRunningDataSaved = pRunningData;
86040  bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES;
86041  for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
86042  ma_uint8 indexCount;
86043  ma_uint32 indexPointSize;
86044  if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) {
86045  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86046  return MA_FALSE;
86047  }
86048  pRunningData += 35;
86049  indexCount = pRunningData[0];
86050  pRunningData += 1;
86051  bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index);
86052  indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;
86053  if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) {
86054  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86055  return MA_FALSE;
86056  }
86057  pRunningData += indexPointSize;
86058  }
86059  pRunningData = pRunningDataSaved;
86060  }
86061  {
86062  char* pRunningTrackData;
86063  pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks);
86064  if (pTrackData == NULL) {
86065  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86066  return MA_FALSE;
86067  }
86068  pRunningTrackData = (char*)pTrackData;
86069  for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
86070  ma_uint8 indexCount;
86071  MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES);
86072  pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;
86073  pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;
86074  indexCount = pRunningData[0];
86075  pRunningData += 1;
86076  pRunningTrackData += 1;
86077  for (iIndex = 0; iIndex < indexCount; ++iIndex) {
86078  ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData;
86079  MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES);
86080  pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;
86081  pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index);
86082  pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset);
86083  }
86084  }
86085  metadata.data.cuesheet.pTrackData = pTrackData;
86086  }
86087  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86088  pRawData = NULL;
86089  onMeta(pUserDataMD, &metadata);
86090  ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks);
86091  pTrackData = NULL;
86092  }
86093  } break;
86094  case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE:
86095  {
86096  if (blockSize < 32) {
86097  return MA_FALSE;
86098  }
86099  if (onMeta) {
86100  void* pRawData;
86101  const char* pRunningData;
86102  const char* pRunningDataEnd;
86103  pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
86104  if (pRawData == NULL) {
86105  return MA_FALSE;
86106  }
86107  if (onRead(pUserData, pRawData, blockSize) != blockSize) {
86108  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86109  return MA_FALSE;
86110  }
86111  metadata.pRawData = pRawData;
86112  metadata.rawDataSize = blockSize;
86113  pRunningData = (const char*)pRawData;
86114  pRunningDataEnd = (const char*)pRawData + blockSize;
86115  metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
86116  metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
86117  if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) {
86118  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86119  return MA_FALSE;
86120  }
86121  metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
86122  metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
86123  if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) {
86124  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86125  return MA_FALSE;
86126  }
86127  metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
86128  metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
86129  metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
86130  metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
86131  metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
86132  metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
86133  metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData;
86134  if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) {
86135  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86136  return MA_FALSE;
86137  }
86138  onMeta(pUserDataMD, &metadata);
86139  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86140  }
86141  } break;
86142  case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING:
86143  {
86144  if (onMeta) {
86145  metadata.data.padding.unused = 0;
86146  if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) {
86147  isLastBlock = MA_TRUE;
86148  } else {
86149  onMeta(pUserDataMD, &metadata);
86150  }
86151  }
86152  } break;
86153  case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID:
86154  {
86155  if (onMeta) {
86156  if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) {
86157  isLastBlock = MA_TRUE;
86158  }
86159  }
86160  } break;
86161  default:
86162  {
86163  if (onMeta) {
86164  void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
86165  if (pRawData == NULL) {
86166  return MA_FALSE;
86167  }
86168  if (onRead(pUserData, pRawData, blockSize) != blockSize) {
86169  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86170  return MA_FALSE;
86171  }
86172  metadata.pRawData = pRawData;
86173  metadata.rawDataSize = blockSize;
86174  onMeta(pUserDataMD, &metadata);
86175  ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
86176  }
86177  } break;
86178  }
86179  if (onMeta == NULL && blockSize > 0) {
86180  if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) {
86181  isLastBlock = MA_TRUE;
86182  }
86183  }
86184  runningFilePos += blockSize;
86185  if (isLastBlock) {
86186  break;
86187  }
86188  }
86189  *pSeektablePos = seektablePos;
86190  *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES;
86191  *pFirstFramePos = runningFilePos;
86192  return MA_TRUE;
86193 }
86194 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)
86195 {
86196  ma_uint8 isLastBlock;
86197  ma_uint8 blockType;
86198  ma_uint32 blockSize;
86199  (void)onSeek;
86200  pInit->container = ma_dr_flac_container_native;
86201  if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
86202  return MA_FALSE;
86203  }
86204  if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
86205  if (!relaxed) {
86206  return MA_FALSE;
86207  } else {
86208  pInit->hasStreamInfoBlock = MA_FALSE;
86209  pInit->hasMetadataBlocks = MA_FALSE;
86210  if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
86211  return MA_FALSE;
86212  }
86213  if (pInit->firstFrameHeader.bitsPerSample == 0) {
86214  return MA_FALSE;
86215  }
86216  pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
86217  pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
86218  pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
86219  pInit->maxBlockSizeInPCMFrames = 65535;
86220  return MA_TRUE;
86221  }
86222  } else {
86223  ma_dr_flac_streaminfo streaminfo;
86224  if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) {
86225  return MA_FALSE;
86226  }
86227  pInit->hasStreamInfoBlock = MA_TRUE;
86228  pInit->sampleRate = streaminfo.sampleRate;
86229  pInit->channels = streaminfo.channels;
86230  pInit->bitsPerSample = streaminfo.bitsPerSample;
86231  pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
86232  pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
86233  pInit->hasMetadataBlocks = !isLastBlock;
86234  if (onMeta) {
86235  ma_dr_flac_metadata metadata;
86236  metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO;
86237  metadata.pRawData = NULL;
86238  metadata.rawDataSize = 0;
86239  metadata.data.streaminfo = streaminfo;
86240  onMeta(pUserDataMD, &metadata);
86241  }
86242  return MA_TRUE;
86243  }
86244 }
86245 #ifndef MA_DR_FLAC_NO_OGG
86246 #define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307
86247 #define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199
86248 typedef enum
86249 {
86250  ma_dr_flac_ogg_recover_on_crc_mismatch,
86251  ma_dr_flac_ogg_fail_on_crc_mismatch
86252 } ma_dr_flac_ogg_crc_mismatch_recovery;
86253 #ifndef MA_DR_FLAC_NO_CRC
86254 static ma_uint32 ma_dr_flac__crc32_table[] = {
86255  0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
86256  0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
86257  0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
86258  0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
86259  0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
86260  0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
86261  0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
86262  0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
86263  0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
86264  0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
86265  0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
86266  0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
86267  0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
86268  0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
86269  0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
86270  0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
86271  0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
86272  0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
86273  0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
86274  0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
86275  0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
86276  0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
86277  0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
86278  0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
86279  0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
86280  0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
86281  0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
86282  0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
86283  0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
86284  0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
86285  0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
86286  0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
86287  0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
86288  0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
86289  0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
86290  0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
86291  0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
86292  0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
86293  0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
86294  0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
86295  0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
86296  0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
86297  0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
86298  0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
86299  0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
86300  0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
86301  0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
86302  0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
86303  0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
86304  0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
86305  0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
86306  0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
86307  0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
86308  0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
86309  0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
86310  0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
86311  0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
86312  0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
86313  0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
86314  0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
86315  0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
86316  0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
86317  0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
86318  0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
86319 };
86320 #endif
86321 static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data)
86322 {
86323 #ifndef MA_DR_FLAC_NO_CRC
86324  return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data];
86325 #else
86326  (void)data;
86327  return crc32;
86328 #endif
86329 }
86330 #if 0
86331 static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data)
86332 {
86333  crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF));
86334  crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF));
86335  crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF));
86336  crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF));
86337  return crc32;
86338 }
86339 static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data)
86340 {
86341  crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF));
86342  crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF));
86343  return crc32;
86344 }
86345 #endif
86346 static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize)
86347 {
86348  ma_uint32 i;
86349  for (i = 0; i < dataSize; ++i) {
86350  crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]);
86351  }
86352  return crc32;
86353 }
86354 static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4])
86355 {
86356  return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
86357 }
86358 static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader)
86359 {
86360  return 27 + pHeader->segmentCount;
86361 }
86362 static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader)
86363 {
86364  ma_uint32 pageBodySize = 0;
86365  int i;
86366  for (i = 0; i < pHeader->segmentCount; ++i) {
86367  pageBodySize += pHeader->segmentTable[i];
86368  }
86369  return pageBodySize;
86370 }
86371 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)
86372 {
86373  ma_uint8 data[23];
86374  ma_uint32 i;
86375  MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32);
86376  if (onRead(pUserData, data, 23) != 23) {
86377  return MA_AT_END;
86378  }
86379  *pBytesRead += 23;
86380  pHeader->capturePattern[0] = 'O';
86381  pHeader->capturePattern[1] = 'g';
86382  pHeader->capturePattern[2] = 'g';
86383  pHeader->capturePattern[3] = 'S';
86384  pHeader->structureVersion = data[0];
86385  pHeader->headerType = data[1];
86386  MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8);
86387  MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4);
86388  MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4);
86389  MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4);
86390  pHeader->segmentCount = data[22];
86391  data[18] = 0;
86392  data[19] = 0;
86393  data[20] = 0;
86394  data[21] = 0;
86395  for (i = 0; i < 23; ++i) {
86396  *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]);
86397  }
86398  if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
86399  return MA_AT_END;
86400  }
86401  *pBytesRead += pHeader->segmentCount;
86402  for (i = 0; i < pHeader->segmentCount; ++i) {
86403  *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
86404  }
86405  return MA_SUCCESS;
86406 }
86407 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)
86408 {
86409  ma_uint8 id[4];
86410  *pBytesRead = 0;
86411  if (onRead(pUserData, id, 4) != 4) {
86412  return MA_AT_END;
86413  }
86414  *pBytesRead += 4;
86415  for (;;) {
86416  if (ma_dr_flac_ogg__is_capture_pattern(id)) {
86417  ma_result result;
86418  *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32;
86419  result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
86420  if (result == MA_SUCCESS) {
86421  return MA_SUCCESS;
86422  } else {
86423  if (result == MA_CRC_MISMATCH) {
86424  continue;
86425  } else {
86426  return result;
86427  }
86428  }
86429  } else {
86430  id[0] = id[1];
86431  id[1] = id[2];
86432  id[2] = id[3];
86433  if (onRead(pUserData, &id[3], 1) != 1) {
86434  return MA_AT_END;
86435  }
86436  *pBytesRead += 1;
86437  }
86438  }
86439 }
86440 typedef struct
86441 {
86442  ma_dr_flac_read_proc onRead;
86443  ma_dr_flac_seek_proc onSeek;
86444  void* pUserData;
86445  ma_uint64 currentBytePos;
86446  ma_uint64 firstBytePos;
86447  ma_uint32 serialNumber;
86448  ma_dr_flac_ogg_page_header bosPageHeader;
86449  ma_dr_flac_ogg_page_header currentPageHeader;
86450  ma_uint32 bytesRemainingInPage;
86451  ma_uint32 pageDataSize;
86452  ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE];
86453 } ma_dr_flac_oggbs;
86454 static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
86455 {
86456  size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
86457  oggbs->currentBytePos += bytesActuallyRead;
86458  return bytesActuallyRead;
86459 }
86460 static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin)
86461 {
86462  if (origin == ma_dr_flac_seek_origin_start) {
86463  if (offset <= 0x7FFFFFFF) {
86464  if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) {
86465  return MA_FALSE;
86466  }
86467  oggbs->currentBytePos = offset;
86468  return MA_TRUE;
86469  } else {
86470  if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) {
86471  return MA_FALSE;
86472  }
86473  oggbs->currentBytePos = offset;
86474  return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current);
86475  }
86476  } else {
86477  while (offset > 0x7FFFFFFF) {
86478  if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) {
86479  return MA_FALSE;
86480  }
86481  oggbs->currentBytePos += 0x7FFFFFFF;
86482  offset -= 0x7FFFFFFF;
86483  }
86484  if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) {
86485  return MA_FALSE;
86486  }
86487  oggbs->currentBytePos += offset;
86488  return MA_TRUE;
86489  }
86490 }
86491 static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod)
86492 {
86493  ma_dr_flac_ogg_page_header header;
86494  for (;;) {
86495  ma_uint32 crc32 = 0;
86496  ma_uint32 bytesRead;
86497  ma_uint32 pageBodySize;
86498 #ifndef MA_DR_FLAC_NO_CRC
86499  ma_uint32 actualCRC32;
86500 #endif
86501  if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
86502  return MA_FALSE;
86503  }
86504  oggbs->currentBytePos += bytesRead;
86505  pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header);
86506  if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) {
86507  continue;
86508  }
86509  if (header.serialNumber != oggbs->serialNumber) {
86510  if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) {
86511  return MA_FALSE;
86512  }
86513  continue;
86514  }
86515  if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
86516  return MA_FALSE;
86517  }
86518  oggbs->pageDataSize = pageBodySize;
86519 #ifndef MA_DR_FLAC_NO_CRC
86520  actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
86521  if (actualCRC32 != header.checksum) {
86522  if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) {
86523  continue;
86524  } else {
86525  ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch);
86526  return MA_FALSE;
86527  }
86528  }
86529 #else
86530  (void)recoveryMethod;
86531 #endif
86532  oggbs->currentPageHeader = header;
86533  oggbs->bytesRemainingInPage = pageBodySize;
86534  return MA_TRUE;
86535  }
86536 }
86537 #if 0
86538 static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg)
86539 {
86540  ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
86541  ma_uint8 iSeg = 0;
86542  ma_uint32 iByte = 0;
86543  while (iByte < bytesConsumedInPage) {
86544  ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
86545  if (iByte + segmentSize > bytesConsumedInPage) {
86546  break;
86547  } else {
86548  iSeg += 1;
86549  iByte += segmentSize;
86550  }
86551  }
86552  *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte);
86553  return iSeg;
86554 }
86555 static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs)
86556 {
86557  for (;;) {
86558  ma_bool32 atEndOfPage = MA_FALSE;
86559  ma_uint8 bytesRemainingInSeg;
86560  ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
86561  ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
86562  for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
86563  ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
86564  if (segmentSize < 255) {
86565  if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
86566  atEndOfPage = MA_TRUE;
86567  }
86568  break;
86569  }
86570  bytesToEndOfPacketOrPage += segmentSize;
86571  }
86572  ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current);
86573  oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
86574  if (atEndOfPage) {
86575  if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) {
86576  return MA_FALSE;
86577  }
86578  if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
86579  return MA_TRUE;
86580  }
86581  } else {
86582  return MA_TRUE;
86583  }
86584  }
86585 }
86586 static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs)
86587 {
86588  return ma_dr_flac_oggbs__seek_to_next_packet(oggbs);
86589 }
86590 #endif
86591 static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
86592 {
86593  ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData;
86594  ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut;
86595  size_t bytesRead = 0;
86596  MA_DR_FLAC_ASSERT(oggbs != NULL);
86597  MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL);
86598  while (bytesRead < bytesToRead) {
86599  size_t bytesRemainingToRead = bytesToRead - bytesRead;
86600  if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
86601  MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
86602  bytesRead += bytesRemainingToRead;
86603  oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead;
86604  break;
86605  }
86606  if (oggbs->bytesRemainingInPage > 0) {
86607  MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
86608  bytesRead += oggbs->bytesRemainingInPage;
86609  pRunningBufferOut += oggbs->bytesRemainingInPage;
86610  oggbs->bytesRemainingInPage = 0;
86611  }
86612  MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0);
86613  if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
86614  break;
86615  }
86616  }
86617  return bytesRead;
86618 }
86619 static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
86620 {
86621  ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData;
86622  int bytesSeeked = 0;
86623  MA_DR_FLAC_ASSERT(oggbs != NULL);
86624  MA_DR_FLAC_ASSERT(offset >= 0);
86625  if (origin == ma_dr_flac_seek_origin_start) {
86626  if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) {
86627  return MA_FALSE;
86628  }
86629  if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) {
86630  return MA_FALSE;
86631  }
86632  return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current);
86633  }
86634  MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current);
86635  while (bytesSeeked < offset) {
86636  int bytesRemainingToSeek = offset - bytesSeeked;
86637  MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0);
86638  if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
86639  bytesSeeked += bytesRemainingToSeek;
86640  (void)bytesSeeked;
86641  oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
86642  break;
86643  }
86644  if (oggbs->bytesRemainingInPage > 0) {
86645  bytesSeeked += (int)oggbs->bytesRemainingInPage;
86646  oggbs->bytesRemainingInPage = 0;
86647  }
86648  MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0);
86649  if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) {
86650  return MA_FALSE;
86651  }
86652  }
86653  return MA_TRUE;
86654 }
86655 static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
86656 {
86657  ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
86658  ma_uint64 originalBytePos;
86659  ma_uint64 runningGranulePosition;
86660  ma_uint64 runningFrameBytePos;
86661  ma_uint64 runningPCMFrameCount;
86662  MA_DR_FLAC_ASSERT(oggbs != NULL);
86663  originalBytePos = oggbs->currentBytePos;
86664  if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {
86665  return MA_FALSE;
86666  }
86667  oggbs->bytesRemainingInPage = 0;
86668  runningGranulePosition = 0;
86669  for (;;) {
86670  if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
86671  ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start);
86672  return MA_FALSE;
86673  }
86674  runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
86675  if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) {
86676  break;
86677  }
86678  if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
86679  if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
86680  ma_uint8 firstBytesInPage[2];
86681  firstBytesInPage[0] = oggbs->pageData[0];
86682  firstBytesInPage[1] = oggbs->pageData[1];
86683  if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) {
86684  runningGranulePosition = oggbs->currentPageHeader.granulePosition;
86685  }
86686  continue;
86687  }
86688  }
86689  }
86690  if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) {
86691  return MA_FALSE;
86692  }
86693  if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
86694  return MA_FALSE;
86695  }
86696  runningPCMFrameCount = runningGranulePosition;
86697  for (;;) {
86698  ma_uint64 firstPCMFrameInFLACFrame = 0;
86699  ma_uint64 lastPCMFrameInFLACFrame = 0;
86700  ma_uint64 pcmFrameCountInThisFrame;
86701  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
86702  return MA_FALSE;
86703  }
86704  ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
86705  pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
86706  if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {
86707  ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
86708  if (result == MA_SUCCESS) {
86709  pFlac->currentPCMFrame = pcmFrameIndex;
86710  pFlac->currentFLACFrame.pcmFramesRemaining = 0;
86711  return MA_TRUE;
86712  } else {
86713  return MA_FALSE;
86714  }
86715  }
86716  if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {
86717  ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
86718  if (result == MA_SUCCESS) {
86719  ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount);
86720  if (pcmFramesToDecode == 0) {
86721  return MA_TRUE;
86722  }
86723  pFlac->currentPCMFrame = runningPCMFrameCount;
86724  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
86725  } else {
86726  if (result == MA_CRC_MISMATCH) {
86727  continue;
86728  } else {
86729  return MA_FALSE;
86730  }
86731  }
86732  } else {
86733  ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
86734  if (result == MA_SUCCESS) {
86735  runningPCMFrameCount += pcmFrameCountInThisFrame;
86736  } else {
86737  if (result == MA_CRC_MISMATCH) {
86738  continue;
86739  } else {
86740  return MA_FALSE;
86741  }
86742  }
86743  }
86744  }
86745 }
86746 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)
86747 {
86748  ma_dr_flac_ogg_page_header header;
86749  ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32;
86750  ma_uint32 bytesRead = 0;
86751  (void)relaxed;
86752  pInit->container = ma_dr_flac_container_ogg;
86753  pInit->oggFirstBytePos = 0;
86754  if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
86755  return MA_FALSE;
86756  }
86757  pInit->runningFilePos += bytesRead;
86758  for (;;) {
86759  int pageBodySize;
86760  if ((header.headerType & 0x02) == 0) {
86761  return MA_FALSE;
86762  }
86763  pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header);
86764  if (pageBodySize == 51) {
86765  ma_uint32 bytesRemainingInPage = pageBodySize;
86766  ma_uint8 packetType;
86767  if (onRead(pUserData, &packetType, 1) != 1) {
86768  return MA_FALSE;
86769  }
86770  bytesRemainingInPage -= 1;
86771  if (packetType == 0x7F) {
86772  ma_uint8 sig[4];
86773  if (onRead(pUserData, sig, 4) != 4) {
86774  return MA_FALSE;
86775  }
86776  bytesRemainingInPage -= 4;
86777  if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
86778  ma_uint8 mappingVersion[2];
86779  if (onRead(pUserData, mappingVersion, 2) != 2) {
86780  return MA_FALSE;
86781  }
86782  if (mappingVersion[0] != 1) {
86783  return MA_FALSE;
86784  }
86785  if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) {
86786  return MA_FALSE;
86787  }
86788  if (onRead(pUserData, sig, 4) != 4) {
86789  return MA_FALSE;
86790  }
86791  if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
86792  ma_dr_flac_streaminfo streaminfo;
86793  ma_uint8 isLastBlock;
86794  ma_uint8 blockType;
86795  ma_uint32 blockSize;
86796  if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
86797  return MA_FALSE;
86798  }
86799  if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
86800  return MA_FALSE;
86801  }
86802  if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) {
86803  pInit->hasStreamInfoBlock = MA_TRUE;
86804  pInit->sampleRate = streaminfo.sampleRate;
86805  pInit->channels = streaminfo.channels;
86806  pInit->bitsPerSample = streaminfo.bitsPerSample;
86807  pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
86808  pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
86809  pInit->hasMetadataBlocks = !isLastBlock;
86810  if (onMeta) {
86811  ma_dr_flac_metadata metadata;
86812  metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO;
86813  metadata.pRawData = NULL;
86814  metadata.rawDataSize = 0;
86815  metadata.data.streaminfo = streaminfo;
86816  onMeta(pUserDataMD, &metadata);
86817  }
86818  pInit->runningFilePos += pageBodySize;
86819  pInit->oggFirstBytePos = pInit->runningFilePos - 79;
86820  pInit->oggSerial = header.serialNumber;
86821  pInit->oggBosHeader = header;
86822  break;
86823  } else {
86824  return MA_FALSE;
86825  }
86826  } else {
86827  return MA_FALSE;
86828  }
86829  } else {
86830  if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) {
86831  return MA_FALSE;
86832  }
86833  }
86834  } else {
86835  if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) {
86836  return MA_FALSE;
86837  }
86838  }
86839  } else {
86840  if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) {
86841  return MA_FALSE;
86842  }
86843  }
86844  pInit->runningFilePos += pageBodySize;
86845  if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
86846  return MA_FALSE;
86847  }
86848  pInit->runningFilePos += bytesRead;
86849  }
86850  pInit->hasMetadataBlocks = MA_TRUE;
86851  return MA_TRUE;
86852 }
86853 #endif
86854 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)
86855 {
86856  ma_bool32 relaxed;
86857  ma_uint8 id[4];
86858  if (pInit == NULL || onRead == NULL || onSeek == NULL) {
86859  return MA_FALSE;
86860  }
86861  MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
86862  pInit->onRead = onRead;
86863  pInit->onSeek = onSeek;
86864  pInit->onMeta = onMeta;
86865  pInit->container = container;
86866  pInit->pUserData = pUserData;
86867  pInit->pUserDataMD = pUserDataMD;
86868  pInit->bs.onRead = onRead;
86869  pInit->bs.onSeek = onSeek;
86870  pInit->bs.pUserData = pUserData;
86871  ma_dr_flac__reset_cache(&pInit->bs);
86872  relaxed = container != ma_dr_flac_container_unknown;
86873  for (;;) {
86874  if (onRead(pUserData, id, 4) != 4) {
86875  return MA_FALSE;
86876  }
86877  pInit->runningFilePos += 4;
86878  if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
86879  ma_uint8 header[6];
86880  ma_uint8 flags;
86881  ma_uint32 headerSize;
86882  if (onRead(pUserData, header, 6) != 6) {
86883  return MA_FALSE;
86884  }
86885  pInit->runningFilePos += 6;
86886  flags = header[1];
86887  MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4);
86888  headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize));
86889  if (flags & 0x10) {
86890  headerSize += 10;
86891  }
86892  if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) {
86893  return MA_FALSE;
86894  }
86895  pInit->runningFilePos += headerSize;
86896  } else {
86897  break;
86898  }
86899  }
86900  if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
86901  return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
86902  }
86903 #ifndef MA_DR_FLAC_NO_OGG
86904  if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
86905  return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
86906  }
86907 #endif
86908  if (relaxed) {
86909  if (container == ma_dr_flac_container_native) {
86910  return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
86911  }
86912 #ifndef MA_DR_FLAC_NO_OGG
86913  if (container == ma_dr_flac_container_ogg) {
86914  return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
86915  }
86916 #endif
86917  }
86918  return MA_FALSE;
86919 }
86920 static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit)
86921 {
86922  MA_DR_FLAC_ASSERT(pFlac != NULL);
86923  MA_DR_FLAC_ASSERT(pInit != NULL);
86924  MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac));
86925  pFlac->bs = pInit->bs;
86926  pFlac->onMeta = pInit->onMeta;
86927  pFlac->pUserDataMD = pInit->pUserDataMD;
86928  pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;
86929  pFlac->sampleRate = pInit->sampleRate;
86930  pFlac->channels = (ma_uint8)pInit->channels;
86931  pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample;
86932  pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount;
86933  pFlac->container = pInit->container;
86934 }
86935 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)
86936 {
86937  ma_dr_flac_init_info init;
86938  ma_uint32 allocationSize;
86939  ma_uint32 wholeSIMDVectorCountPerChannel;
86940  ma_uint32 decodedSamplesAllocationSize;
86941 #ifndef MA_DR_FLAC_NO_OGG
86942  ma_dr_flac_oggbs* pOggbs = NULL;
86943 #endif
86944  ma_uint64 firstFramePos;
86945  ma_uint64 seektablePos;
86946  ma_uint32 seekpointCount;
86947  ma_allocation_callbacks allocationCallbacks;
86948  ma_dr_flac* pFlac;
86949  ma_dr_flac__init_cpu_caps();
86950  if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
86951  return NULL;
86952  }
86953  if (pAllocationCallbacks != NULL) {
86954  allocationCallbacks = *pAllocationCallbacks;
86955  if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {
86956  return NULL;
86957  }
86958  } else {
86959  allocationCallbacks.pUserData = NULL;
86960  allocationCallbacks.onMalloc = ma_dr_flac__malloc_default;
86961  allocationCallbacks.onRealloc = ma_dr_flac__realloc_default;
86962  allocationCallbacks.onFree = ma_dr_flac__free_default;
86963  }
86964  allocationSize = sizeof(ma_dr_flac);
86965  if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) {
86966  wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32)));
86967  } else {
86968  wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1;
86969  }
86970  decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
86971  allocationSize += decodedSamplesAllocationSize;
86972  allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE;
86973 #ifndef MA_DR_FLAC_NO_OGG
86974  if (init.container == ma_dr_flac_container_ogg) {
86975  allocationSize += sizeof(ma_dr_flac_oggbs);
86976  pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks);
86977  if (pOggbs == NULL) {
86978  return NULL;
86979  }
86980  MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs));
86981  pOggbs->onRead = onRead;
86982  pOggbs->onSeek = onSeek;
86983  pOggbs->pUserData = pUserData;
86984  pOggbs->currentBytePos = init.oggFirstBytePos;
86985  pOggbs->firstBytePos = init.oggFirstBytePos;
86986  pOggbs->serialNumber = init.oggSerial;
86987  pOggbs->bosPageHeader = init.oggBosHeader;
86988  pOggbs->bytesRemainingInPage = 0;
86989  }
86990 #endif
86991  firstFramePos = 42;
86992  seektablePos = 0;
86993  seekpointCount = 0;
86994  if (init.hasMetadataBlocks) {
86995  ma_dr_flac_read_proc onReadOverride = onRead;
86996  ma_dr_flac_seek_proc onSeekOverride = onSeek;
86997  void* pUserDataOverride = pUserData;
86998 #ifndef MA_DR_FLAC_NO_OGG
86999  if (init.container == ma_dr_flac_container_ogg) {
87000  onReadOverride = ma_dr_flac__on_read_ogg;
87001  onSeekOverride = ma_dr_flac__on_seek_ogg;
87002  pUserDataOverride = (void*)pOggbs;
87003  }
87004 #endif
87005  if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {
87006  #ifndef MA_DR_FLAC_NO_OGG
87007  ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
87008  #endif
87009  return NULL;
87010  }
87011  allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint);
87012  }
87013  pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks);
87014  if (pFlac == NULL) {
87015  #ifndef MA_DR_FLAC_NO_OGG
87016  ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
87017  #endif
87018  return NULL;
87019  }
87020  ma_dr_flac__init_from_info(pFlac, &init);
87021  pFlac->allocationCallbacks = allocationCallbacks;
87022  pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE);
87023 #ifndef MA_DR_FLAC_NO_OGG
87024  if (init.container == ma_dr_flac_container_ogg) {
87025  ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint)));
87026  MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs));
87027  ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
87028  pOggbs = NULL;
87029  pFlac->bs.onRead = ma_dr_flac__on_read_ogg;
87030  pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg;
87031  pFlac->bs.pUserData = (void*)pInternalOggbs;
87032  pFlac->_oggbs = (void*)pInternalOggbs;
87033  }
87034 #endif
87035  pFlac->firstFLACFramePosInBytes = firstFramePos;
87036 #ifndef MA_DR_FLAC_NO_OGG
87037  if (init.container == ma_dr_flac_container_ogg)
87038  {
87039  pFlac->pSeekpoints = NULL;
87040  pFlac->seekpointCount = 0;
87041  }
87042  else
87043 #endif
87044  {
87045  if (seektablePos != 0) {
87046  pFlac->seekpointCount = seekpointCount;
87047  pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
87048  MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL);
87049  MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL);
87050  if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) {
87051  ma_uint32 iSeekpoint;
87052  for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) {
87053  if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) {
87054  pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);
87055  pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);
87056  pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);
87057  } else {
87058  pFlac->pSeekpoints = NULL;
87059  pFlac->seekpointCount = 0;
87060  break;
87061  }
87062  }
87063  if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) {
87064  ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
87065  return NULL;
87066  }
87067  } else {
87068  pFlac->pSeekpoints = NULL;
87069  pFlac->seekpointCount = 0;
87070  }
87071  }
87072  }
87073  if (!init.hasStreamInfoBlock) {
87074  pFlac->currentFLACFrame.header = init.firstFrameHeader;
87075  for (;;) {
87076  ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
87077  if (result == MA_SUCCESS) {
87078  break;
87079  } else {
87080  if (result == MA_CRC_MISMATCH) {
87081  if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
87082  ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
87083  return NULL;
87084  }
87085  continue;
87086  } else {
87087  ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
87088  return NULL;
87089  }
87090  }
87091  }
87092  }
87093  return pFlac;
87094 }
87095 #ifndef MA_DR_FLAC_NO_STDIO
87096 #include <stdio.h>
87097 #ifndef MA_DR_FLAC_NO_WCHAR
87098 #include <wchar.h>
87099 #endif
87100 static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
87101 {
87102  return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
87103 }
87104 static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
87105 {
87106  MA_DR_FLAC_ASSERT(offset >= 0);
87107  return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
87108 }
87109 MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks)
87110 {
87111  ma_dr_flac* pFlac;
87112  FILE* pFile;
87113  if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) {
87114  return NULL;
87115  }
87116  pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
87117  if (pFlac == NULL) {
87118  fclose(pFile);
87119  return NULL;
87120  }
87121  return pFlac;
87122 }
87123 #ifndef MA_DR_FLAC_NO_WCHAR
87124 MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks)
87125 {
87126  ma_dr_flac* pFlac;
87127  FILE* pFile;
87128  if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
87129  return NULL;
87130  }
87131  pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
87132  if (pFlac == NULL) {
87133  fclose(pFile);
87134  return NULL;
87135  }
87136  return pFlac;
87137 }
87138 #endif
87139 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)
87140 {
87141  ma_dr_flac* pFlac;
87142  FILE* pFile;
87143  if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) {
87144  return NULL;
87145  }
87146  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);
87147  if (pFlac == NULL) {
87148  fclose(pFile);
87149  return pFlac;
87150  }
87151  return pFlac;
87152 }
87153 #ifndef MA_DR_FLAC_NO_WCHAR
87154 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)
87155 {
87156  ma_dr_flac* pFlac;
87157  FILE* pFile;
87158  if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
87159  return NULL;
87160  }
87161  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);
87162  if (pFlac == NULL) {
87163  fclose(pFile);
87164  return pFlac;
87165  }
87166  return pFlac;
87167 }
87168 #endif
87169 #endif
87170 static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
87171 {
87172  ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;
87173  size_t bytesRemaining;
87174  MA_DR_FLAC_ASSERT(memoryStream != NULL);
87175  MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);
87176  bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
87177  if (bytesToRead > bytesRemaining) {
87178  bytesToRead = bytesRemaining;
87179  }
87180  if (bytesToRead > 0) {
87181  MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
87182  memoryStream->currentReadPos += bytesToRead;
87183  }
87184  return bytesToRead;
87185 }
87186 static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
87187 {
87188  ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;
87189  MA_DR_FLAC_ASSERT(memoryStream != NULL);
87190  MA_DR_FLAC_ASSERT(offset >= 0);
87191  if (offset > (ma_int64)memoryStream->dataSize) {
87192  return MA_FALSE;
87193  }
87194  if (origin == ma_dr_flac_seek_origin_current) {
87195  if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
87196  memoryStream->currentReadPos += offset;
87197  } else {
87198  return MA_FALSE;
87199  }
87200  } else {
87201  if ((ma_uint32)offset <= memoryStream->dataSize) {
87202  memoryStream->currentReadPos = offset;
87203  } else {
87204  return MA_FALSE;
87205  }
87206  }
87207  return MA_TRUE;
87208 }
87209 MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
87210 {
87211  ma_dr_flac__memory_stream memoryStream;
87212  ma_dr_flac* pFlac;
87213  memoryStream.data = (const ma_uint8*)pData;
87214  memoryStream.dataSize = dataSize;
87215  memoryStream.currentReadPos = 0;
87216  pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks);
87217  if (pFlac == NULL) {
87218  return NULL;
87219  }
87220  pFlac->memoryStream = memoryStream;
87221 #ifndef MA_DR_FLAC_NO_OGG
87222  if (pFlac->container == ma_dr_flac_container_ogg)
87223  {
87224  ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
87225  oggbs->pUserData = &pFlac->memoryStream;
87226  }
87227  else
87228 #endif
87229  {
87230  pFlac->bs.pUserData = &pFlac->memoryStream;
87231  }
87232  return pFlac;
87233 }
87234 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)
87235 {
87236  ma_dr_flac__memory_stream memoryStream;
87237  ma_dr_flac* pFlac;
87238  memoryStream.data = (const ma_uint8*)pData;
87239  memoryStream.dataSize = dataSize;
87240  memoryStream.currentReadPos = 0;
87241  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);
87242  if (pFlac == NULL) {
87243  return NULL;
87244  }
87245  pFlac->memoryStream = memoryStream;
87246 #ifndef MA_DR_FLAC_NO_OGG
87247  if (pFlac->container == ma_dr_flac_container_ogg)
87248  {
87249  ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
87250  oggbs->pUserData = &pFlac->memoryStream;
87251  }
87252  else
87253 #endif
87254  {
87255  pFlac->bs.pUserData = &pFlac->memoryStream;
87256  }
87257  return pFlac;
87258 }
87259 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)
87260 {
87261  return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
87262 }
87263 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)
87264 {
87265  return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);
87266 }
87267 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)
87268 {
87269  return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
87270 }
87271 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)
87272 {
87273  return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
87274 }
87275 MA_API void ma_dr_flac_close(ma_dr_flac* pFlac)
87276 {
87277  if (pFlac == NULL) {
87278  return;
87279  }
87280 #ifndef MA_DR_FLAC_NO_STDIO
87281  if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) {
87282  fclose((FILE*)pFlac->bs.pUserData);
87283  }
87284 #ifndef MA_DR_FLAC_NO_OGG
87285  if (pFlac->container == ma_dr_flac_container_ogg) {
87286  ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
87287  MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg);
87288  if (oggbs->onRead == ma_dr_flac__on_read_stdio) {
87289  fclose((FILE*)oggbs->pUserData);
87290  }
87291  }
87292 #endif
87293 #endif
87294  ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);
87295 }
87296 #if 0
87297 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)
87298 {
87299  ma_uint64 i;
87300  for (i = 0; i < frameCount; ++i) {
87301  ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87302  ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87303  ma_uint32 right = left - side;
87304  pOutputSamples[i*2+0] = (ma_int32)left;
87305  pOutputSamples[i*2+1] = (ma_int32)right;
87306  }
87307 }
87308 #endif
87309 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)
87310 {
87311  ma_uint64 i;
87312  ma_uint64 frameCount4 = frameCount >> 2;
87313  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87314  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87315  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87316  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87317  for (i = 0; i < frameCount4; ++i) {
87318  ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
87319  ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
87320  ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
87321  ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
87322  ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
87323  ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
87324  ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
87325  ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
87326  ma_uint32 right0 = left0 - side0;
87327  ma_uint32 right1 = left1 - side1;
87328  ma_uint32 right2 = left2 - side2;
87329  ma_uint32 right3 = left3 - side3;
87330  pOutputSamples[i*8+0] = (ma_int32)left0;
87331  pOutputSamples[i*8+1] = (ma_int32)right0;
87332  pOutputSamples[i*8+2] = (ma_int32)left1;
87333  pOutputSamples[i*8+3] = (ma_int32)right1;
87334  pOutputSamples[i*8+4] = (ma_int32)left2;
87335  pOutputSamples[i*8+5] = (ma_int32)right2;
87336  pOutputSamples[i*8+6] = (ma_int32)left3;
87337  pOutputSamples[i*8+7] = (ma_int32)right3;
87338  }
87339  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87340  ma_uint32 left = pInputSamples0U32[i] << shift0;
87341  ma_uint32 side = pInputSamples1U32[i] << shift1;
87342  ma_uint32 right = left - side;
87343  pOutputSamples[i*2+0] = (ma_int32)left;
87344  pOutputSamples[i*2+1] = (ma_int32)right;
87345  }
87346 }
87347 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
87348 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)
87349 {
87350  ma_uint64 i;
87351  ma_uint64 frameCount4 = frameCount >> 2;
87352  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87353  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87354  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87355  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87356  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
87357  for (i = 0; i < frameCount4; ++i) {
87358  __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
87359  __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
87360  __m128i right = _mm_sub_epi32(left, side);
87361  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87362  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87363  }
87364  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87365  ma_uint32 left = pInputSamples0U32[i] << shift0;
87366  ma_uint32 side = pInputSamples1U32[i] << shift1;
87367  ma_uint32 right = left - side;
87368  pOutputSamples[i*2+0] = (ma_int32)left;
87369  pOutputSamples[i*2+1] = (ma_int32)right;
87370  }
87371 }
87372 #endif
87373 #if defined(MA_DR_FLAC_SUPPORT_NEON)
87374 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)
87375 {
87376  ma_uint64 i;
87377  ma_uint64 frameCount4 = frameCount >> 2;
87378  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87379  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87380  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87381  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87382  int32x4_t shift0_4;
87383  int32x4_t shift1_4;
87384  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
87385  shift0_4 = vdupq_n_s32(shift0);
87386  shift1_4 = vdupq_n_s32(shift1);
87387  for (i = 0; i < frameCount4; ++i) {
87388  uint32x4_t left;
87389  uint32x4_t side;
87390  uint32x4_t right;
87391  left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
87392  side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
87393  right = vsubq_u32(left, side);
87394  ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
87395  }
87396  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87397  ma_uint32 left = pInputSamples0U32[i] << shift0;
87398  ma_uint32 side = pInputSamples1U32[i] << shift1;
87399  ma_uint32 right = left - side;
87400  pOutputSamples[i*2+0] = (ma_int32)left;
87401  pOutputSamples[i*2+1] = (ma_int32)right;
87402  }
87403 }
87404 #endif
87405 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)
87406 {
87407 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
87408  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
87409  ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87410  } else
87411 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
87412  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
87413  ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87414  } else
87415 #endif
87416  {
87417 #if 0
87418  ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87419 #else
87420  ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87421 #endif
87422  }
87423 }
87424 #if 0
87425 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)
87426 {
87427  ma_uint64 i;
87428  for (i = 0; i < frameCount; ++i) {
87429  ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87430  ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87431  ma_uint32 left = right + side;
87432  pOutputSamples[i*2+0] = (ma_int32)left;
87433  pOutputSamples[i*2+1] = (ma_int32)right;
87434  }
87435 }
87436 #endif
87437 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)
87438 {
87439  ma_uint64 i;
87440  ma_uint64 frameCount4 = frameCount >> 2;
87441  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87442  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87443  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87444  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87445  for (i = 0; i < frameCount4; ++i) {
87446  ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
87447  ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
87448  ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
87449  ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
87450  ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
87451  ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
87452  ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
87453  ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
87454  ma_uint32 left0 = right0 + side0;
87455  ma_uint32 left1 = right1 + side1;
87456  ma_uint32 left2 = right2 + side2;
87457  ma_uint32 left3 = right3 + side3;
87458  pOutputSamples[i*8+0] = (ma_int32)left0;
87459  pOutputSamples[i*8+1] = (ma_int32)right0;
87460  pOutputSamples[i*8+2] = (ma_int32)left1;
87461  pOutputSamples[i*8+3] = (ma_int32)right1;
87462  pOutputSamples[i*8+4] = (ma_int32)left2;
87463  pOutputSamples[i*8+5] = (ma_int32)right2;
87464  pOutputSamples[i*8+6] = (ma_int32)left3;
87465  pOutputSamples[i*8+7] = (ma_int32)right3;
87466  }
87467  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87468  ma_uint32 side = pInputSamples0U32[i] << shift0;
87469  ma_uint32 right = pInputSamples1U32[i] << shift1;
87470  ma_uint32 left = right + side;
87471  pOutputSamples[i*2+0] = (ma_int32)left;
87472  pOutputSamples[i*2+1] = (ma_int32)right;
87473  }
87474 }
87475 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
87476 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)
87477 {
87478  ma_uint64 i;
87479  ma_uint64 frameCount4 = frameCount >> 2;
87480  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87481  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87482  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87483  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87484  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
87485  for (i = 0; i < frameCount4; ++i) {
87486  __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
87487  __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
87488  __m128i left = _mm_add_epi32(right, side);
87489  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87490  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87491  }
87492  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87493  ma_uint32 side = pInputSamples0U32[i] << shift0;
87494  ma_uint32 right = pInputSamples1U32[i] << shift1;
87495  ma_uint32 left = right + side;
87496  pOutputSamples[i*2+0] = (ma_int32)left;
87497  pOutputSamples[i*2+1] = (ma_int32)right;
87498  }
87499 }
87500 #endif
87501 #if defined(MA_DR_FLAC_SUPPORT_NEON)
87502 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)
87503 {
87504  ma_uint64 i;
87505  ma_uint64 frameCount4 = frameCount >> 2;
87506  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87507  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87508  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87509  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87510  int32x4_t shift0_4;
87511  int32x4_t shift1_4;
87512  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
87513  shift0_4 = vdupq_n_s32(shift0);
87514  shift1_4 = vdupq_n_s32(shift1);
87515  for (i = 0; i < frameCount4; ++i) {
87516  uint32x4_t side;
87517  uint32x4_t right;
87518  uint32x4_t left;
87519  side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
87520  right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
87521  left = vaddq_u32(right, side);
87522  ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
87523  }
87524  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87525  ma_uint32 side = pInputSamples0U32[i] << shift0;
87526  ma_uint32 right = pInputSamples1U32[i] << shift1;
87527  ma_uint32 left = right + side;
87528  pOutputSamples[i*2+0] = (ma_int32)left;
87529  pOutputSamples[i*2+1] = (ma_int32)right;
87530  }
87531 }
87532 #endif
87533 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)
87534 {
87535 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
87536  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
87537  ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87538  } else
87539 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
87540  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
87541  ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87542  } else
87543 #endif
87544  {
87545 #if 0
87546  ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87547 #else
87548  ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87549 #endif
87550  }
87551 }
87552 #if 0
87553 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)
87554 {
87555  for (ma_uint64 i = 0; i < frameCount; ++i) {
87556  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87557  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87558  mid = (mid << 1) | (side & 0x01);
87559  pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample);
87560  pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample);
87561  }
87562 }
87563 #endif
87564 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)
87565 {
87566  ma_uint64 i;
87567  ma_uint64 frameCount4 = frameCount >> 2;
87568  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87569  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87570  ma_int32 shift = unusedBitsPerSample;
87571  if (shift > 0) {
87572  shift -= 1;
87573  for (i = 0; i < frameCount4; ++i) {
87574  ma_uint32 temp0L;
87575  ma_uint32 temp1L;
87576  ma_uint32 temp2L;
87577  ma_uint32 temp3L;
87578  ma_uint32 temp0R;
87579  ma_uint32 temp1R;
87580  ma_uint32 temp2R;
87581  ma_uint32 temp3R;
87582  ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87583  ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87584  ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87585  ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87586  ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87587  ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87588  ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87589  ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87590  mid0 = (mid0 << 1) | (side0 & 0x01);
87591  mid1 = (mid1 << 1) | (side1 & 0x01);
87592  mid2 = (mid2 << 1) | (side2 & 0x01);
87593  mid3 = (mid3 << 1) | (side3 & 0x01);
87594  temp0L = (mid0 + side0) << shift;
87595  temp1L = (mid1 + side1) << shift;
87596  temp2L = (mid2 + side2) << shift;
87597  temp3L = (mid3 + side3) << shift;
87598  temp0R = (mid0 - side0) << shift;
87599  temp1R = (mid1 - side1) << shift;
87600  temp2R = (mid2 - side2) << shift;
87601  temp3R = (mid3 - side3) << shift;
87602  pOutputSamples[i*8+0] = (ma_int32)temp0L;
87603  pOutputSamples[i*8+1] = (ma_int32)temp0R;
87604  pOutputSamples[i*8+2] = (ma_int32)temp1L;
87605  pOutputSamples[i*8+3] = (ma_int32)temp1R;
87606  pOutputSamples[i*8+4] = (ma_int32)temp2L;
87607  pOutputSamples[i*8+5] = (ma_int32)temp2R;
87608  pOutputSamples[i*8+6] = (ma_int32)temp3L;
87609  pOutputSamples[i*8+7] = (ma_int32)temp3R;
87610  }
87611  } else {
87612  for (i = 0; i < frameCount4; ++i) {
87613  ma_uint32 temp0L;
87614  ma_uint32 temp1L;
87615  ma_uint32 temp2L;
87616  ma_uint32 temp3L;
87617  ma_uint32 temp0R;
87618  ma_uint32 temp1R;
87619  ma_uint32 temp2R;
87620  ma_uint32 temp3R;
87621  ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87622  ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87623  ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87624  ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87625  ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87626  ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87627  ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87628  ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87629  mid0 = (mid0 << 1) | (side0 & 0x01);
87630  mid1 = (mid1 << 1) | (side1 & 0x01);
87631  mid2 = (mid2 << 1) | (side2 & 0x01);
87632  mid3 = (mid3 << 1) | (side3 & 0x01);
87633  temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1);
87634  temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1);
87635  temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1);
87636  temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1);
87637  temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1);
87638  temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1);
87639  temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1);
87640  temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1);
87641  pOutputSamples[i*8+0] = (ma_int32)temp0L;
87642  pOutputSamples[i*8+1] = (ma_int32)temp0R;
87643  pOutputSamples[i*8+2] = (ma_int32)temp1L;
87644  pOutputSamples[i*8+3] = (ma_int32)temp1R;
87645  pOutputSamples[i*8+4] = (ma_int32)temp2L;
87646  pOutputSamples[i*8+5] = (ma_int32)temp2R;
87647  pOutputSamples[i*8+6] = (ma_int32)temp3L;
87648  pOutputSamples[i*8+7] = (ma_int32)temp3R;
87649  }
87650  }
87651  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87652  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87653  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87654  mid = (mid << 1) | (side & 0x01);
87655  pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample);
87656  pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample);
87657  }
87658 }
87659 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
87660 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)
87661 {
87662  ma_uint64 i;
87663  ma_uint64 frameCount4 = frameCount >> 2;
87664  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87665  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87666  ma_int32 shift = unusedBitsPerSample;
87667  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
87668  if (shift == 0) {
87669  for (i = 0; i < frameCount4; ++i) {
87670  __m128i mid;
87671  __m128i side;
87672  __m128i left;
87673  __m128i right;
87674  mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87675  side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87676  mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
87677  left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
87678  right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
87679  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87680  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87681  }
87682  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87683  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87684  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87685  mid = (mid << 1) | (side & 0x01);
87686  pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1;
87687  pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1;
87688  }
87689  } else {
87690  shift -= 1;
87691  for (i = 0; i < frameCount4; ++i) {
87692  __m128i mid;
87693  __m128i side;
87694  __m128i left;
87695  __m128i right;
87696  mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87697  side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87698  mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
87699  left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
87700  right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
87701  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87702  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87703  }
87704  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87705  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87706  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87707  mid = (mid << 1) | (side & 0x01);
87708  pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift);
87709  pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift);
87710  }
87711  }
87712 }
87713 #endif
87714 #if defined(MA_DR_FLAC_SUPPORT_NEON)
87715 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)
87716 {
87717  ma_uint64 i;
87718  ma_uint64 frameCount4 = frameCount >> 2;
87719  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87720  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87721  ma_int32 shift = unusedBitsPerSample;
87722  int32x4_t wbpsShift0_4;
87723  int32x4_t wbpsShift1_4;
87724  uint32x4_t one4;
87725  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
87726  wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87727  wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87728  one4 = vdupq_n_u32(1);
87729  if (shift == 0) {
87730  for (i = 0; i < frameCount4; ++i) {
87731  uint32x4_t mid;
87732  uint32x4_t side;
87733  int32x4_t left;
87734  int32x4_t right;
87735  mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
87736  side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
87737  mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
87738  left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
87739  right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
87740  ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
87741  }
87742  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87743  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87744  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87745  mid = (mid << 1) | (side & 0x01);
87746  pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1;
87747  pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1;
87748  }
87749  } else {
87750  int32x4_t shift4;
87751  shift -= 1;
87752  shift4 = vdupq_n_s32(shift);
87753  for (i = 0; i < frameCount4; ++i) {
87754  uint32x4_t mid;
87755  uint32x4_t side;
87756  int32x4_t left;
87757  int32x4_t right;
87758  mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
87759  side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
87760  mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
87761  left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
87762  right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
87763  ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
87764  }
87765  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87766  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87767  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87768  mid = (mid << 1) | (side & 0x01);
87769  pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift);
87770  pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift);
87771  }
87772  }
87773 }
87774 #endif
87775 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)
87776 {
87777 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
87778  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
87779  ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87780  } else
87781 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
87782  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
87783  ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87784  } else
87785 #endif
87786  {
87787 #if 0
87788  ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87789 #else
87790  ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87791 #endif
87792  }
87793 }
87794 #if 0
87795 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)
87796 {
87797  for (ma_uint64 i = 0; i < frameCount; ++i) {
87798  pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));
87799  pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));
87800  }
87801 }
87802 #endif
87803 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)
87804 {
87805  ma_uint64 i;
87806  ma_uint64 frameCount4 = frameCount >> 2;
87807  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87808  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87809  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87810  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87811  for (i = 0; i < frameCount4; ++i) {
87812  ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
87813  ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
87814  ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
87815  ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
87816  ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
87817  ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
87818  ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
87819  ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
87820  pOutputSamples[i*8+0] = (ma_int32)tempL0;
87821  pOutputSamples[i*8+1] = (ma_int32)tempR0;
87822  pOutputSamples[i*8+2] = (ma_int32)tempL1;
87823  pOutputSamples[i*8+3] = (ma_int32)tempR1;
87824  pOutputSamples[i*8+4] = (ma_int32)tempL2;
87825  pOutputSamples[i*8+5] = (ma_int32)tempR2;
87826  pOutputSamples[i*8+6] = (ma_int32)tempL3;
87827  pOutputSamples[i*8+7] = (ma_int32)tempR3;
87828  }
87829  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87830  pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
87831  pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
87832  }
87833 }
87834 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
87835 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)
87836 {
87837  ma_uint64 i;
87838  ma_uint64 frameCount4 = frameCount >> 2;
87839  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87840  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87841  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87842  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87843  for (i = 0; i < frameCount4; ++i) {
87844  __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
87845  __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
87846  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87847  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87848  }
87849  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87850  pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
87851  pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
87852  }
87853 }
87854 #endif
87855 #if defined(MA_DR_FLAC_SUPPORT_NEON)
87856 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)
87857 {
87858  ma_uint64 i;
87859  ma_uint64 frameCount4 = frameCount >> 2;
87860  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87861  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87862  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87863  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87864  int32x4_t shift4_0 = vdupq_n_s32(shift0);
87865  int32x4_t shift4_1 = vdupq_n_s32(shift1);
87866  for (i = 0; i < frameCount4; ++i) {
87867  int32x4_t left;
87868  int32x4_t right;
87869  left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0));
87870  right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1));
87871  ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
87872  }
87873  for (i = (frameCount4 << 2); i < frameCount; ++i) {
87874  pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
87875  pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
87876  }
87877 }
87878 #endif
87879 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)
87880 {
87881 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
87882  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
87883  ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87884  } else
87885 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
87886  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
87887  ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87888  } else
87889 #endif
87890  {
87891 #if 0
87892  ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87893 #else
87894  ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87895 #endif
87896  }
87897 }
87898 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut)
87899 {
87900  ma_uint64 framesRead;
87901  ma_uint32 unusedBitsPerSample;
87902  if (pFlac == NULL || framesToRead == 0) {
87903  return 0;
87904  }
87905  if (pBufferOut == NULL) {
87906  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
87907  }
87908  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
87909  unusedBitsPerSample = 32 - pFlac->bitsPerSample;
87910  framesRead = 0;
87911  while (framesToRead > 0) {
87912  if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
87913  if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
87914  break;
87915  }
87916  } else {
87917  unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
87918  ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
87919  ma_uint64 frameCountThisIteration = framesToRead;
87920  if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
87921  frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
87922  }
87923  if (channelCount == 2) {
87924  const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
87925  const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
87926  switch (pFlac->currentFLACFrame.header.channelAssignment)
87927  {
87928  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
87929  {
87930  ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
87931  } break;
87932  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
87933  {
87934  ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
87935  } break;
87936  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
87937  {
87938  ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
87939  } break;
87940  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
87941  default:
87942  {
87943  ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
87944  } break;
87945  }
87946  } else {
87947  ma_uint64 i;
87948  for (i = 0; i < frameCountThisIteration; ++i) {
87949  unsigned int j;
87950  for (j = 0; j < channelCount; ++j) {
87951  pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
87952  }
87953  }
87954  }
87955  framesRead += frameCountThisIteration;
87956  pBufferOut += frameCountThisIteration * channelCount;
87957  framesToRead -= frameCountThisIteration;
87958  pFlac->currentPCMFrame += frameCountThisIteration;
87959  pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration;
87960  }
87961  }
87962  return framesRead;
87963 }
87964 #if 0
87965 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)
87966 {
87967  ma_uint64 i;
87968  for (i = 0; i < frameCount; ++i) {
87969  ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87970  ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87971  ma_uint32 right = left - side;
87972  left >>= 16;
87973  right >>= 16;
87974  pOutputSamples[i*2+0] = (ma_int16)left;
87975  pOutputSamples[i*2+1] = (ma_int16)right;
87976  }
87977 }
87978 #endif
87979 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)
87980 {
87981  ma_uint64 i;
87982  ma_uint64 frameCount4 = frameCount >> 2;
87983  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
87984  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
87985  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87986  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87987  for (i = 0; i < frameCount4; ++i) {
87988  ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
87989  ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
87990  ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
87991  ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
87992  ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
87993  ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
87994  ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
87995  ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
87996  ma_uint32 right0 = left0 - side0;
87997  ma_uint32 right1 = left1 - side1;
87998  ma_uint32 right2 = left2 - side2;
87999  ma_uint32 right3 = left3 - side3;
88000  left0 >>= 16;
88001  left1 >>= 16;
88002  left2 >>= 16;
88003  left3 >>= 16;
88004  right0 >>= 16;
88005  right1 >>= 16;
88006  right2 >>= 16;
88007  right3 >>= 16;
88008  pOutputSamples[i*8+0] = (ma_int16)left0;
88009  pOutputSamples[i*8+1] = (ma_int16)right0;
88010  pOutputSamples[i*8+2] = (ma_int16)left1;
88011  pOutputSamples[i*8+3] = (ma_int16)right1;
88012  pOutputSamples[i*8+4] = (ma_int16)left2;
88013  pOutputSamples[i*8+5] = (ma_int16)right2;
88014  pOutputSamples[i*8+6] = (ma_int16)left3;
88015  pOutputSamples[i*8+7] = (ma_int16)right3;
88016  }
88017  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88018  ma_uint32 left = pInputSamples0U32[i] << shift0;
88019  ma_uint32 side = pInputSamples1U32[i] << shift1;
88020  ma_uint32 right = left - side;
88021  left >>= 16;
88022  right >>= 16;
88023  pOutputSamples[i*2+0] = (ma_int16)left;
88024  pOutputSamples[i*2+1] = (ma_int16)right;
88025  }
88026 }
88027 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88028 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)
88029 {
88030  ma_uint64 i;
88031  ma_uint64 frameCount4 = frameCount >> 2;
88032  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88033  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88034  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88035  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88036  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88037  for (i = 0; i < frameCount4; ++i) {
88038  __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88039  __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88040  __m128i right = _mm_sub_epi32(left, side);
88041  left = _mm_srai_epi32(left, 16);
88042  right = _mm_srai_epi32(right, 16);
88043  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
88044  }
88045  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88046  ma_uint32 left = pInputSamples0U32[i] << shift0;
88047  ma_uint32 side = pInputSamples1U32[i] << shift1;
88048  ma_uint32 right = left - side;
88049  left >>= 16;
88050  right >>= 16;
88051  pOutputSamples[i*2+0] = (ma_int16)left;
88052  pOutputSamples[i*2+1] = (ma_int16)right;
88053  }
88054 }
88055 #endif
88056 #if defined(MA_DR_FLAC_SUPPORT_NEON)
88057 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)
88058 {
88059  ma_uint64 i;
88060  ma_uint64 frameCount4 = frameCount >> 2;
88061  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88062  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88063  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88064  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88065  int32x4_t shift0_4;
88066  int32x4_t shift1_4;
88067  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88068  shift0_4 = vdupq_n_s32(shift0);
88069  shift1_4 = vdupq_n_s32(shift1);
88070  for (i = 0; i < frameCount4; ++i) {
88071  uint32x4_t left;
88072  uint32x4_t side;
88073  uint32x4_t right;
88074  left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
88075  side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
88076  right = vsubq_u32(left, side);
88077  left = vshrq_n_u32(left, 16);
88078  right = vshrq_n_u32(right, 16);
88079  ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
88080  }
88081  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88082  ma_uint32 left = pInputSamples0U32[i] << shift0;
88083  ma_uint32 side = pInputSamples1U32[i] << shift1;
88084  ma_uint32 right = left - side;
88085  left >>= 16;
88086  right >>= 16;
88087  pOutputSamples[i*2+0] = (ma_int16)left;
88088  pOutputSamples[i*2+1] = (ma_int16)right;
88089  }
88090 }
88091 #endif
88092 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)
88093 {
88094 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88095  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88096  ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88097  } else
88098 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
88099  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88100  ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88101  } else
88102 #endif
88103  {
88104 #if 0
88105  ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88106 #else
88107  ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88108 #endif
88109  }
88110 }
88111 #if 0
88112 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)
88113 {
88114  ma_uint64 i;
88115  for (i = 0; i < frameCount; ++i) {
88116  ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88117  ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88118  ma_uint32 left = right + side;
88119  left >>= 16;
88120  right >>= 16;
88121  pOutputSamples[i*2+0] = (ma_int16)left;
88122  pOutputSamples[i*2+1] = (ma_int16)right;
88123  }
88124 }
88125 #endif
88126 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)
88127 {
88128  ma_uint64 i;
88129  ma_uint64 frameCount4 = frameCount >> 2;
88130  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88131  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88132  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88133  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88134  for (i = 0; i < frameCount4; ++i) {
88135  ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
88136  ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
88137  ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
88138  ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
88139  ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
88140  ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
88141  ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
88142  ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
88143  ma_uint32 left0 = right0 + side0;
88144  ma_uint32 left1 = right1 + side1;
88145  ma_uint32 left2 = right2 + side2;
88146  ma_uint32 left3 = right3 + side3;
88147  left0 >>= 16;
88148  left1 >>= 16;
88149  left2 >>= 16;
88150  left3 >>= 16;
88151  right0 >>= 16;
88152  right1 >>= 16;
88153  right2 >>= 16;
88154  right3 >>= 16;
88155  pOutputSamples[i*8+0] = (ma_int16)left0;
88156  pOutputSamples[i*8+1] = (ma_int16)right0;
88157  pOutputSamples[i*8+2] = (ma_int16)left1;
88158  pOutputSamples[i*8+3] = (ma_int16)right1;
88159  pOutputSamples[i*8+4] = (ma_int16)left2;
88160  pOutputSamples[i*8+5] = (ma_int16)right2;
88161  pOutputSamples[i*8+6] = (ma_int16)left3;
88162  pOutputSamples[i*8+7] = (ma_int16)right3;
88163  }
88164  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88165  ma_uint32 side = pInputSamples0U32[i] << shift0;
88166  ma_uint32 right = pInputSamples1U32[i] << shift1;
88167  ma_uint32 left = right + side;
88168  left >>= 16;
88169  right >>= 16;
88170  pOutputSamples[i*2+0] = (ma_int16)left;
88171  pOutputSamples[i*2+1] = (ma_int16)right;
88172  }
88173 }
88174 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88175 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)
88176 {
88177  ma_uint64 i;
88178  ma_uint64 frameCount4 = frameCount >> 2;
88179  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88180  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88181  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88182  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88183  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88184  for (i = 0; i < frameCount4; ++i) {
88185  __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88186  __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88187  __m128i left = _mm_add_epi32(right, side);
88188  left = _mm_srai_epi32(left, 16);
88189  right = _mm_srai_epi32(right, 16);
88190  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
88191  }
88192  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88193  ma_uint32 side = pInputSamples0U32[i] << shift0;
88194  ma_uint32 right = pInputSamples1U32[i] << shift1;
88195  ma_uint32 left = right + side;
88196  left >>= 16;
88197  right >>= 16;
88198  pOutputSamples[i*2+0] = (ma_int16)left;
88199  pOutputSamples[i*2+1] = (ma_int16)right;
88200  }
88201 }
88202 #endif
88203 #if defined(MA_DR_FLAC_SUPPORT_NEON)
88204 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)
88205 {
88206  ma_uint64 i;
88207  ma_uint64 frameCount4 = frameCount >> 2;
88208  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88209  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88210  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88211  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88212  int32x4_t shift0_4;
88213  int32x4_t shift1_4;
88214  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88215  shift0_4 = vdupq_n_s32(shift0);
88216  shift1_4 = vdupq_n_s32(shift1);
88217  for (i = 0; i < frameCount4; ++i) {
88218  uint32x4_t side;
88219  uint32x4_t right;
88220  uint32x4_t left;
88221  side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
88222  right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
88223  left = vaddq_u32(right, side);
88224  left = vshrq_n_u32(left, 16);
88225  right = vshrq_n_u32(right, 16);
88226  ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
88227  }
88228  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88229  ma_uint32 side = pInputSamples0U32[i] << shift0;
88230  ma_uint32 right = pInputSamples1U32[i] << shift1;
88231  ma_uint32 left = right + side;
88232  left >>= 16;
88233  right >>= 16;
88234  pOutputSamples[i*2+0] = (ma_int16)left;
88235  pOutputSamples[i*2+1] = (ma_int16)right;
88236  }
88237 }
88238 #endif
88239 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)
88240 {
88241 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88242  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88243  ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88244  } else
88245 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
88246  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88247  ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88248  } else
88249 #endif
88250  {
88251 #if 0
88252  ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88253 #else
88254  ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88255 #endif
88256  }
88257 }
88258 #if 0
88259 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)
88260 {
88261  for (ma_uint64 i = 0; i < frameCount; ++i) {
88262  ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88263  ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88264  mid = (mid << 1) | (side & 0x01);
88265  pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
88266  pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
88267  }
88268 }
88269 #endif
88270 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)
88271 {
88272  ma_uint64 i;
88273  ma_uint64 frameCount4 = frameCount >> 2;
88274  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88275  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88276  ma_uint32 shift = unusedBitsPerSample;
88277  if (shift > 0) {
88278  shift -= 1;
88279  for (i = 0; i < frameCount4; ++i) {
88280  ma_uint32 temp0L;
88281  ma_uint32 temp1L;
88282  ma_uint32 temp2L;
88283  ma_uint32 temp3L;
88284  ma_uint32 temp0R;
88285  ma_uint32 temp1R;
88286  ma_uint32 temp2R;
88287  ma_uint32 temp3R;
88288  ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88289  ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88290  ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88291  ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88292  ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88293  ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88294  ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88295  ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88296  mid0 = (mid0 << 1) | (side0 & 0x01);
88297  mid1 = (mid1 << 1) | (side1 & 0x01);
88298  mid2 = (mid2 << 1) | (side2 & 0x01);
88299  mid3 = (mid3 << 1) | (side3 & 0x01);
88300  temp0L = (mid0 + side0) << shift;
88301  temp1L = (mid1 + side1) << shift;
88302  temp2L = (mid2 + side2) << shift;
88303  temp3L = (mid3 + side3) << shift;
88304  temp0R = (mid0 - side0) << shift;
88305  temp1R = (mid1 - side1) << shift;
88306  temp2R = (mid2 - side2) << shift;
88307  temp3R = (mid3 - side3) << shift;
88308  temp0L >>= 16;
88309  temp1L >>= 16;
88310  temp2L >>= 16;
88311  temp3L >>= 16;
88312  temp0R >>= 16;
88313  temp1R >>= 16;
88314  temp2R >>= 16;
88315  temp3R >>= 16;
88316  pOutputSamples[i*8+0] = (ma_int16)temp0L;
88317  pOutputSamples[i*8+1] = (ma_int16)temp0R;
88318  pOutputSamples[i*8+2] = (ma_int16)temp1L;
88319  pOutputSamples[i*8+3] = (ma_int16)temp1R;
88320  pOutputSamples[i*8+4] = (ma_int16)temp2L;
88321  pOutputSamples[i*8+5] = (ma_int16)temp2R;
88322  pOutputSamples[i*8+6] = (ma_int16)temp3L;
88323  pOutputSamples[i*8+7] = (ma_int16)temp3R;
88324  }
88325  } else {
88326  for (i = 0; i < frameCount4; ++i) {
88327  ma_uint32 temp0L;
88328  ma_uint32 temp1L;
88329  ma_uint32 temp2L;
88330  ma_uint32 temp3L;
88331  ma_uint32 temp0R;
88332  ma_uint32 temp1R;
88333  ma_uint32 temp2R;
88334  ma_uint32 temp3R;
88335  ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88336  ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88337  ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88338  ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88339  ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88340  ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88341  ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88342  ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88343  mid0 = (mid0 << 1) | (side0 & 0x01);
88344  mid1 = (mid1 << 1) | (side1 & 0x01);
88345  mid2 = (mid2 << 1) | (side2 & 0x01);
88346  mid3 = (mid3 << 1) | (side3 & 0x01);
88347  temp0L = ((ma_int32)(mid0 + side0) >> 1);
88348  temp1L = ((ma_int32)(mid1 + side1) >> 1);
88349  temp2L = ((ma_int32)(mid2 + side2) >> 1);
88350  temp3L = ((ma_int32)(mid3 + side3) >> 1);
88351  temp0R = ((ma_int32)(mid0 - side0) >> 1);
88352  temp1R = ((ma_int32)(mid1 - side1) >> 1);
88353  temp2R = ((ma_int32)(mid2 - side2) >> 1);
88354  temp3R = ((ma_int32)(mid3 - side3) >> 1);
88355  temp0L >>= 16;
88356  temp1L >>= 16;
88357  temp2L >>= 16;
88358  temp3L >>= 16;
88359  temp0R >>= 16;
88360  temp1R >>= 16;
88361  temp2R >>= 16;
88362  temp3R >>= 16;
88363  pOutputSamples[i*8+0] = (ma_int16)temp0L;
88364  pOutputSamples[i*8+1] = (ma_int16)temp0R;
88365  pOutputSamples[i*8+2] = (ma_int16)temp1L;
88366  pOutputSamples[i*8+3] = (ma_int16)temp1R;
88367  pOutputSamples[i*8+4] = (ma_int16)temp2L;
88368  pOutputSamples[i*8+5] = (ma_int16)temp2R;
88369  pOutputSamples[i*8+6] = (ma_int16)temp3L;
88370  pOutputSamples[i*8+7] = (ma_int16)temp3R;
88371  }
88372  }
88373  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88374  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88375  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88376  mid = (mid << 1) | (side & 0x01);
88377  pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
88378  pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
88379  }
88380 }
88381 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88382 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)
88383 {
88384  ma_uint64 i;
88385  ma_uint64 frameCount4 = frameCount >> 2;
88386  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88387  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88388  ma_uint32 shift = unusedBitsPerSample;
88389  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88390  if (shift == 0) {
88391  for (i = 0; i < frameCount4; ++i) {
88392  __m128i mid;
88393  __m128i side;
88394  __m128i left;
88395  __m128i right;
88396  mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88397  side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88398  mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
88399  left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
88400  right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
88401  left = _mm_srai_epi32(left, 16);
88402  right = _mm_srai_epi32(right, 16);
88403  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
88404  }
88405  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88406  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88407  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88408  mid = (mid << 1) | (side & 0x01);
88409  pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16);
88410  pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16);
88411  }
88412  } else {
88413  shift -= 1;
88414  for (i = 0; i < frameCount4; ++i) {
88415  __m128i mid;
88416  __m128i side;
88417  __m128i left;
88418  __m128i right;
88419  mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88420  side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88421  mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
88422  left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
88423  right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
88424  left = _mm_srai_epi32(left, 16);
88425  right = _mm_srai_epi32(right, 16);
88426  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
88427  }
88428  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88429  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88430  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88431  mid = (mid << 1) | (side & 0x01);
88432  pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16);
88433  pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16);
88434  }
88435  }
88436 }
88437 #endif
88438 #if defined(MA_DR_FLAC_SUPPORT_NEON)
88439 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)
88440 {
88441  ma_uint64 i;
88442  ma_uint64 frameCount4 = frameCount >> 2;
88443  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88444  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88445  ma_uint32 shift = unusedBitsPerSample;
88446  int32x4_t wbpsShift0_4;
88447  int32x4_t wbpsShift1_4;
88448  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88449  wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88450  wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88451  if (shift == 0) {
88452  for (i = 0; i < frameCount4; ++i) {
88453  uint32x4_t mid;
88454  uint32x4_t side;
88455  int32x4_t left;
88456  int32x4_t right;
88457  mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
88458  side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
88459  mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
88460  left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
88461  right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
88462  left = vshrq_n_s32(left, 16);
88463  right = vshrq_n_s32(right, 16);
88464  ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
88465  }
88466  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88467  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88468  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88469  mid = (mid << 1) | (side & 0x01);
88470  pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16);
88471  pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16);
88472  }
88473  } else {
88474  int32x4_t shift4;
88475  shift -= 1;
88476  shift4 = vdupq_n_s32(shift);
88477  for (i = 0; i < frameCount4; ++i) {
88478  uint32x4_t mid;
88479  uint32x4_t side;
88480  int32x4_t left;
88481  int32x4_t right;
88482  mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
88483  side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
88484  mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
88485  left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
88486  right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
88487  left = vshrq_n_s32(left, 16);
88488  right = vshrq_n_s32(right, 16);
88489  ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
88490  }
88491  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88492  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88493  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88494  mid = (mid << 1) | (side & 0x01);
88495  pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16);
88496  pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16);
88497  }
88498  }
88499 }
88500 #endif
88501 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)
88502 {
88503 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88504  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88505  ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88506  } else
88507 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
88508  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88509  ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88510  } else
88511 #endif
88512  {
88513 #if 0
88514  ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88515 #else
88516  ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88517 #endif
88518  }
88519 }
88520 #if 0
88521 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)
88522 {
88523  for (ma_uint64 i = 0; i < frameCount; ++i) {
88524  pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);
88525  pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);
88526  }
88527 }
88528 #endif
88529 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)
88530 {
88531  ma_uint64 i;
88532  ma_uint64 frameCount4 = frameCount >> 2;
88533  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88534  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88535  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88536  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88537  for (i = 0; i < frameCount4; ++i) {
88538  ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
88539  ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
88540  ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
88541  ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
88542  ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
88543  ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
88544  ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
88545  ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
88546  tempL0 >>= 16;
88547  tempL1 >>= 16;
88548  tempL2 >>= 16;
88549  tempL3 >>= 16;
88550  tempR0 >>= 16;
88551  tempR1 >>= 16;
88552  tempR2 >>= 16;
88553  tempR3 >>= 16;
88554  pOutputSamples[i*8+0] = (ma_int16)tempL0;
88555  pOutputSamples[i*8+1] = (ma_int16)tempR0;
88556  pOutputSamples[i*8+2] = (ma_int16)tempL1;
88557  pOutputSamples[i*8+3] = (ma_int16)tempR1;
88558  pOutputSamples[i*8+4] = (ma_int16)tempL2;
88559  pOutputSamples[i*8+5] = (ma_int16)tempR2;
88560  pOutputSamples[i*8+6] = (ma_int16)tempL3;
88561  pOutputSamples[i*8+7] = (ma_int16)tempR3;
88562  }
88563  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88564  pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
88565  pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
88566  }
88567 }
88568 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88569 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)
88570 {
88571  ma_uint64 i;
88572  ma_uint64 frameCount4 = frameCount >> 2;
88573  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88574  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88575  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88576  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88577  for (i = 0; i < frameCount4; ++i) {
88578  __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88579  __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88580  left = _mm_srai_epi32(left, 16);
88581  right = _mm_srai_epi32(right, 16);
88582  _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
88583  }
88584  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88585  pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
88586  pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
88587  }
88588 }
88589 #endif
88590 #if defined(MA_DR_FLAC_SUPPORT_NEON)
88591 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)
88592 {
88593  ma_uint64 i;
88594  ma_uint64 frameCount4 = frameCount >> 2;
88595  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88596  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88597  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88598  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88599  int32x4_t shift0_4 = vdupq_n_s32(shift0);
88600  int32x4_t shift1_4 = vdupq_n_s32(shift1);
88601  for (i = 0; i < frameCount4; ++i) {
88602  int32x4_t left;
88603  int32x4_t right;
88604  left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
88605  right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
88606  left = vshrq_n_s32(left, 16);
88607  right = vshrq_n_s32(right, 16);
88608  ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
88609  }
88610  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88611  pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
88612  pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
88613  }
88614 }
88615 #endif
88616 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)
88617 {
88618 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88619  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88620  ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88621  } else
88622 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
88623  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88624  ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88625  } else
88626 #endif
88627  {
88628 #if 0
88629  ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88630 #else
88631  ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88632 #endif
88633  }
88634 }
88635 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut)
88636 {
88637  ma_uint64 framesRead;
88638  ma_uint32 unusedBitsPerSample;
88639  if (pFlac == NULL || framesToRead == 0) {
88640  return 0;
88641  }
88642  if (pBufferOut == NULL) {
88643  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
88644  }
88645  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
88646  unusedBitsPerSample = 32 - pFlac->bitsPerSample;
88647  framesRead = 0;
88648  while (framesToRead > 0) {
88649  if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
88650  if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
88651  break;
88652  }
88653  } else {
88654  unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
88655  ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
88656  ma_uint64 frameCountThisIteration = framesToRead;
88657  if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
88658  frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
88659  }
88660  if (channelCount == 2) {
88661  const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
88662  const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
88663  switch (pFlac->currentFLACFrame.header.channelAssignment)
88664  {
88665  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
88666  {
88667  ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88668  } break;
88669  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
88670  {
88671  ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88672  } break;
88673  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
88674  {
88675  ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88676  } break;
88677  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
88678  default:
88679  {
88680  ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88681  } break;
88682  }
88683  } else {
88684  ma_uint64 i;
88685  for (i = 0; i < frameCountThisIteration; ++i) {
88686  unsigned int j;
88687  for (j = 0; j < channelCount; ++j) {
88688  ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
88689  pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16);
88690  }
88691  }
88692  }
88693  framesRead += frameCountThisIteration;
88694  pBufferOut += frameCountThisIteration * channelCount;
88695  framesToRead -= frameCountThisIteration;
88696  pFlac->currentPCMFrame += frameCountThisIteration;
88697  pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration;
88698  }
88699  }
88700  return framesRead;
88701 }
88702 #if 0
88703 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)
88704 {
88705  ma_uint64 i;
88706  for (i = 0; i < frameCount; ++i) {
88707  ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88708  ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88709  ma_uint32 right = left - side;
88710  pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0);
88711  pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0);
88712  }
88713 }
88714 #endif
88715 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)
88716 {
88717  ma_uint64 i;
88718  ma_uint64 frameCount4 = frameCount >> 2;
88719  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88720  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88721  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88722  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88723  float factor = 1 / 2147483648.0;
88724  for (i = 0; i < frameCount4; ++i) {
88725  ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
88726  ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
88727  ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
88728  ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
88729  ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
88730  ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
88731  ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
88732  ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
88733  ma_uint32 right0 = left0 - side0;
88734  ma_uint32 right1 = left1 - side1;
88735  ma_uint32 right2 = left2 - side2;
88736  ma_uint32 right3 = left3 - side3;
88737  pOutputSamples[i*8+0] = (ma_int32)left0 * factor;
88738  pOutputSamples[i*8+1] = (ma_int32)right0 * factor;
88739  pOutputSamples[i*8+2] = (ma_int32)left1 * factor;
88740  pOutputSamples[i*8+3] = (ma_int32)right1 * factor;
88741  pOutputSamples[i*8+4] = (ma_int32)left2 * factor;
88742  pOutputSamples[i*8+5] = (ma_int32)right2 * factor;
88743  pOutputSamples[i*8+6] = (ma_int32)left3 * factor;
88744  pOutputSamples[i*8+7] = (ma_int32)right3 * factor;
88745  }
88746  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88747  ma_uint32 left = pInputSamples0U32[i] << shift0;
88748  ma_uint32 side = pInputSamples1U32[i] << shift1;
88749  ma_uint32 right = left - side;
88750  pOutputSamples[i*2+0] = (ma_int32)left * factor;
88751  pOutputSamples[i*2+1] = (ma_int32)right * factor;
88752  }
88753 }
88754 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88755 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)
88756 {
88757  ma_uint64 i;
88758  ma_uint64 frameCount4 = frameCount >> 2;
88759  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88760  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88761  ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
88762  ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
88763  __m128 factor;
88764  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88765  factor = _mm_set1_ps(1.0f / 8388608.0f);
88766  for (i = 0; i < frameCount4; ++i) {
88767  __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88768  __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88769  __m128i right = _mm_sub_epi32(left, side);
88770  __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
88771  __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
88772  _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
88773  _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
88774  }
88775  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88776  ma_uint32 left = pInputSamples0U32[i] << shift0;
88777  ma_uint32 side = pInputSamples1U32[i] << shift1;
88778  ma_uint32 right = left - side;
88779  pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
88780  pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
88781  }
88782 }
88783 #endif
88784 #if defined(MA_DR_FLAC_SUPPORT_NEON)
88785 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)
88786 {
88787  ma_uint64 i;
88788  ma_uint64 frameCount4 = frameCount >> 2;
88789  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88790  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88791  ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
88792  ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
88793  float32x4_t factor4;
88794  int32x4_t shift0_4;
88795  int32x4_t shift1_4;
88796  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88797  factor4 = vdupq_n_f32(1.0f / 8388608.0f);
88798  shift0_4 = vdupq_n_s32(shift0);
88799  shift1_4 = vdupq_n_s32(shift1);
88800  for (i = 0; i < frameCount4; ++i) {
88801  uint32x4_t left;
88802  uint32x4_t side;
88803  uint32x4_t right;
88804  float32x4_t leftf;
88805  float32x4_t rightf;
88806  left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
88807  side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
88808  right = vsubq_u32(left, side);
88809  leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
88810  rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
88811  ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
88812  }
88813  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88814  ma_uint32 left = pInputSamples0U32[i] << shift0;
88815  ma_uint32 side = pInputSamples1U32[i] << shift1;
88816  ma_uint32 right = left - side;
88817  pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
88818  pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
88819  }
88820 }
88821 #endif
88822 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)
88823 {
88824 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88825  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88826  ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88827  } else
88828 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
88829  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88830  ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88831  } else
88832 #endif
88833  {
88834 #if 0
88835  ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88836 #else
88837  ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88838 #endif
88839  }
88840 }
88841 #if 0
88842 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)
88843 {
88844  ma_uint64 i;
88845  for (i = 0; i < frameCount; ++i) {
88846  ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88847  ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88848  ma_uint32 left = right + side;
88849  pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0);
88850  pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0);
88851  }
88852 }
88853 #endif
88854 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)
88855 {
88856  ma_uint64 i;
88857  ma_uint64 frameCount4 = frameCount >> 2;
88858  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88859  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88860  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88861  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88862  float factor = 1 / 2147483648.0;
88863  for (i = 0; i < frameCount4; ++i) {
88864  ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
88865  ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
88866  ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
88867  ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
88868  ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
88869  ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
88870  ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
88871  ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
88872  ma_uint32 left0 = right0 + side0;
88873  ma_uint32 left1 = right1 + side1;
88874  ma_uint32 left2 = right2 + side2;
88875  ma_uint32 left3 = right3 + side3;
88876  pOutputSamples[i*8+0] = (ma_int32)left0 * factor;
88877  pOutputSamples[i*8+1] = (ma_int32)right0 * factor;
88878  pOutputSamples[i*8+2] = (ma_int32)left1 * factor;
88879  pOutputSamples[i*8+3] = (ma_int32)right1 * factor;
88880  pOutputSamples[i*8+4] = (ma_int32)left2 * factor;
88881  pOutputSamples[i*8+5] = (ma_int32)right2 * factor;
88882  pOutputSamples[i*8+6] = (ma_int32)left3 * factor;
88883  pOutputSamples[i*8+7] = (ma_int32)right3 * factor;
88884  }
88885  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88886  ma_uint32 side = pInputSamples0U32[i] << shift0;
88887  ma_uint32 right = pInputSamples1U32[i] << shift1;
88888  ma_uint32 left = right + side;
88889  pOutputSamples[i*2+0] = (ma_int32)left * factor;
88890  pOutputSamples[i*2+1] = (ma_int32)right * factor;
88891  }
88892 }
88893 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88894 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)
88895 {
88896  ma_uint64 i;
88897  ma_uint64 frameCount4 = frameCount >> 2;
88898  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88899  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88900  ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
88901  ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
88902  __m128 factor;
88903  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88904  factor = _mm_set1_ps(1.0f / 8388608.0f);
88905  for (i = 0; i < frameCount4; ++i) {
88906  __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88907  __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88908  __m128i left = _mm_add_epi32(right, side);
88909  __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
88910  __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
88911  _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
88912  _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
88913  }
88914  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88915  ma_uint32 side = pInputSamples0U32[i] << shift0;
88916  ma_uint32 right = pInputSamples1U32[i] << shift1;
88917  ma_uint32 left = right + side;
88918  pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
88919  pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
88920  }
88921 }
88922 #endif
88923 #if defined(MA_DR_FLAC_SUPPORT_NEON)
88924 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)
88925 {
88926  ma_uint64 i;
88927  ma_uint64 frameCount4 = frameCount >> 2;
88928  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88929  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88930  ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
88931  ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
88932  float32x4_t factor4;
88933  int32x4_t shift0_4;
88934  int32x4_t shift1_4;
88935  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
88936  factor4 = vdupq_n_f32(1.0f / 8388608.0f);
88937  shift0_4 = vdupq_n_s32(shift0);
88938  shift1_4 = vdupq_n_s32(shift1);
88939  for (i = 0; i < frameCount4; ++i) {
88940  uint32x4_t side;
88941  uint32x4_t right;
88942  uint32x4_t left;
88943  float32x4_t leftf;
88944  float32x4_t rightf;
88945  side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
88946  right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
88947  left = vaddq_u32(right, side);
88948  leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
88949  rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
88950  ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
88951  }
88952  for (i = (frameCount4 << 2); i < frameCount; ++i) {
88953  ma_uint32 side = pInputSamples0U32[i] << shift0;
88954  ma_uint32 right = pInputSamples1U32[i] << shift1;
88955  ma_uint32 left = right + side;
88956  pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
88957  pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
88958  }
88959 }
88960 #endif
88961 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)
88962 {
88963 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
88964  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88965  ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88966  } else
88967 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
88968  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88969  ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88970  } else
88971 #endif
88972  {
88973 #if 0
88974  ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88975 #else
88976  ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88977 #endif
88978  }
88979 }
88980 #if 0
88981 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)
88982 {
88983  for (ma_uint64 i = 0; i < frameCount; ++i) {
88984  ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88985  ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88986  mid = (mid << 1) | (side & 0x01);
88987  pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
88988  pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
88989  }
88990 }
88991 #endif
88992 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)
88993 {
88994  ma_uint64 i;
88995  ma_uint64 frameCount4 = frameCount >> 2;
88996  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
88997  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
88998  ma_uint32 shift = unusedBitsPerSample;
88999  float factor = 1 / 2147483648.0;
89000  if (shift > 0) {
89001  shift -= 1;
89002  for (i = 0; i < frameCount4; ++i) {
89003  ma_uint32 temp0L;
89004  ma_uint32 temp1L;
89005  ma_uint32 temp2L;
89006  ma_uint32 temp3L;
89007  ma_uint32 temp0R;
89008  ma_uint32 temp1R;
89009  ma_uint32 temp2R;
89010  ma_uint32 temp3R;
89011  ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89012  ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89013  ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89014  ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89015  ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89016  ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89017  ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89018  ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89019  mid0 = (mid0 << 1) | (side0 & 0x01);
89020  mid1 = (mid1 << 1) | (side1 & 0x01);
89021  mid2 = (mid2 << 1) | (side2 & 0x01);
89022  mid3 = (mid3 << 1) | (side3 & 0x01);
89023  temp0L = (mid0 + side0) << shift;
89024  temp1L = (mid1 + side1) << shift;
89025  temp2L = (mid2 + side2) << shift;
89026  temp3L = (mid3 + side3) << shift;
89027  temp0R = (mid0 - side0) << shift;
89028  temp1R = (mid1 - side1) << shift;
89029  temp2R = (mid2 - side2) << shift;
89030  temp3R = (mid3 - side3) << shift;
89031  pOutputSamples[i*8+0] = (ma_int32)temp0L * factor;
89032  pOutputSamples[i*8+1] = (ma_int32)temp0R * factor;
89033  pOutputSamples[i*8+2] = (ma_int32)temp1L * factor;
89034  pOutputSamples[i*8+3] = (ma_int32)temp1R * factor;
89035  pOutputSamples[i*8+4] = (ma_int32)temp2L * factor;
89036  pOutputSamples[i*8+5] = (ma_int32)temp2R * factor;
89037  pOutputSamples[i*8+6] = (ma_int32)temp3L * factor;
89038  pOutputSamples[i*8+7] = (ma_int32)temp3R * factor;
89039  }
89040  } else {
89041  for (i = 0; i < frameCount4; ++i) {
89042  ma_uint32 temp0L;
89043  ma_uint32 temp1L;
89044  ma_uint32 temp2L;
89045  ma_uint32 temp3L;
89046  ma_uint32 temp0R;
89047  ma_uint32 temp1R;
89048  ma_uint32 temp2R;
89049  ma_uint32 temp3R;
89050  ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89051  ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89052  ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89053  ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89054  ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89055  ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89056  ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89057  ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89058  mid0 = (mid0 << 1) | (side0 & 0x01);
89059  mid1 = (mid1 << 1) | (side1 & 0x01);
89060  mid2 = (mid2 << 1) | (side2 & 0x01);
89061  mid3 = (mid3 << 1) | (side3 & 0x01);
89062  temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1);
89063  temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1);
89064  temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1);
89065  temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1);
89066  temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1);
89067  temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1);
89068  temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1);
89069  temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1);
89070  pOutputSamples[i*8+0] = (ma_int32)temp0L * factor;
89071  pOutputSamples[i*8+1] = (ma_int32)temp0R * factor;
89072  pOutputSamples[i*8+2] = (ma_int32)temp1L * factor;
89073  pOutputSamples[i*8+3] = (ma_int32)temp1R * factor;
89074  pOutputSamples[i*8+4] = (ma_int32)temp2L * factor;
89075  pOutputSamples[i*8+5] = (ma_int32)temp2R * factor;
89076  pOutputSamples[i*8+6] = (ma_int32)temp3L * factor;
89077  pOutputSamples[i*8+7] = (ma_int32)temp3R * factor;
89078  }
89079  }
89080  for (i = (frameCount4 << 2); i < frameCount; ++i) {
89081  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89082  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89083  mid = (mid << 1) | (side & 0x01);
89084  pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor;
89085  pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor;
89086  }
89087 }
89088 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
89089 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)
89090 {
89091  ma_uint64 i;
89092  ma_uint64 frameCount4 = frameCount >> 2;
89093  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89094  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89095  ma_uint32 shift = unusedBitsPerSample - 8;
89096  float factor;
89097  __m128 factor128;
89098  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
89099  factor = 1.0f / 8388608.0f;
89100  factor128 = _mm_set1_ps(factor);
89101  if (shift == 0) {
89102  for (i = 0; i < frameCount4; ++i) {
89103  __m128i mid;
89104  __m128i side;
89105  __m128i tempL;
89106  __m128i tempR;
89107  __m128 leftf;
89108  __m128 rightf;
89109  mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89110  side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89111  mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
89112  tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
89113  tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
89114  leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
89115  rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
89116  _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
89117  _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
89118  }
89119  for (i = (frameCount4 << 2); i < frameCount; ++i) {
89120  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89121  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89122  mid = (mid << 1) | (side & 0x01);
89123  pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor;
89124  pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor;
89125  }
89126  } else {
89127  shift -= 1;
89128  for (i = 0; i < frameCount4; ++i) {
89129  __m128i mid;
89130  __m128i side;
89131  __m128i tempL;
89132  __m128i tempR;
89133  __m128 leftf;
89134  __m128 rightf;
89135  mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89136  side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89137  mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
89138  tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
89139  tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
89140  leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
89141  rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
89142  _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
89143  _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
89144  }
89145  for (i = (frameCount4 << 2); i < frameCount; ++i) {
89146  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89147  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89148  mid = (mid << 1) | (side & 0x01);
89149  pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor;
89150  pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor;
89151  }
89152  }
89153 }
89154 #endif
89155 #if defined(MA_DR_FLAC_SUPPORT_NEON)
89156 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)
89157 {
89158  ma_uint64 i;
89159  ma_uint64 frameCount4 = frameCount >> 2;
89160  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89161  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89162  ma_uint32 shift = unusedBitsPerSample - 8;
89163  float factor;
89164  float32x4_t factor4;
89165  int32x4_t shift4;
89166  int32x4_t wbps0_4;
89167  int32x4_t wbps1_4;
89168  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
89169  factor = 1.0f / 8388608.0f;
89170  factor4 = vdupq_n_f32(factor);
89171  wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89172  wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89173  if (shift == 0) {
89174  for (i = 0; i < frameCount4; ++i) {
89175  int32x4_t lefti;
89176  int32x4_t righti;
89177  float32x4_t leftf;
89178  float32x4_t rightf;
89179  uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
89180  uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
89181  mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
89182  lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
89183  righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
89184  leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
89185  rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
89186  ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
89187  }
89188  for (i = (frameCount4 << 2); i < frameCount; ++i) {
89189  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89190  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89191  mid = (mid << 1) | (side & 0x01);
89192  pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor;
89193  pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor;
89194  }
89195  } else {
89196  shift -= 1;
89197  shift4 = vdupq_n_s32(shift);
89198  for (i = 0; i < frameCount4; ++i) {
89199  uint32x4_t mid;
89200  uint32x4_t side;
89201  int32x4_t lefti;
89202  int32x4_t righti;
89203  float32x4_t leftf;
89204  float32x4_t rightf;
89205  mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
89206  side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
89207  mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
89208  lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
89209  righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
89210  leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
89211  rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
89212  ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
89213  }
89214  for (i = (frameCount4 << 2); i < frameCount; ++i) {
89215  ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89216  ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89217  mid = (mid << 1) | (side & 0x01);
89218  pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor;
89219  pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor;
89220  }
89221  }
89222 }
89223 #endif
89224 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)
89225 {
89226 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
89227  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
89228  ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89229  } else
89230 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
89231  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
89232  ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89233  } else
89234 #endif
89235  {
89236 #if 0
89237  ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89238 #else
89239  ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89240 #endif
89241  }
89242 }
89243 #if 0
89244 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)
89245 {
89246  for (ma_uint64 i = 0; i < frameCount; ++i) {
89247  pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
89248  pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
89249  }
89250 }
89251 #endif
89252 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)
89253 {
89254  ma_uint64 i;
89255  ma_uint64 frameCount4 = frameCount >> 2;
89256  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89257  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89258  ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89259  ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89260  float factor = 1 / 2147483648.0;
89261  for (i = 0; i < frameCount4; ++i) {
89262  ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
89263  ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
89264  ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
89265  ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
89266  ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
89267  ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
89268  ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
89269  ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
89270  pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor;
89271  pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor;
89272  pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor;
89273  pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor;
89274  pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor;
89275  pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor;
89276  pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor;
89277  pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor;
89278  }
89279  for (i = (frameCount4 << 2); i < frameCount; ++i) {
89280  pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
89281  pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
89282  }
89283 }
89284 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
89285 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)
89286 {
89287  ma_uint64 i;
89288  ma_uint64 frameCount4 = frameCount >> 2;
89289  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89290  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89291  ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
89292  ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
89293  float factor = 1.0f / 8388608.0f;
89294  __m128 factor128 = _mm_set1_ps(factor);
89295  for (i = 0; i < frameCount4; ++i) {
89296  __m128i lefti;
89297  __m128i righti;
89298  __m128 leftf;
89299  __m128 rightf;
89300  lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
89301  righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
89302  leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128);
89303  rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);
89304  _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
89305  _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
89306  }
89307  for (i = (frameCount4 << 2); i < frameCount; ++i) {
89308  pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
89309  pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
89310  }
89311 }
89312 #endif
89313 #if defined(MA_DR_FLAC_SUPPORT_NEON)
89314 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)
89315 {
89316  ma_uint64 i;
89317  ma_uint64 frameCount4 = frameCount >> 2;
89318  const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89319  const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89320  ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
89321  ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
89322  float factor = 1.0f / 8388608.0f;
89323  float32x4_t factor4 = vdupq_n_f32(factor);
89324  int32x4_t shift0_4 = vdupq_n_s32(shift0);
89325  int32x4_t shift1_4 = vdupq_n_s32(shift1);
89326  for (i = 0; i < frameCount4; ++i) {
89327  int32x4_t lefti;
89328  int32x4_t righti;
89329  float32x4_t leftf;
89330  float32x4_t rightf;
89331  lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
89332  righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
89333  leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
89334  rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
89335  ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
89336  }
89337  for (i = (frameCount4 << 2); i < frameCount; ++i) {
89338  pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
89339  pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
89340  }
89341 }
89342 #endif
89343 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)
89344 {
89345 #if defined(MA_DR_FLAC_SUPPORT_SSE2)
89346  if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
89347  ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89348  } else
89349 #elif defined(MA_DR_FLAC_SUPPORT_NEON)
89350  if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
89351  ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89352  } else
89353 #endif
89354  {
89355 #if 0
89356  ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89357 #else
89358  ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89359 #endif
89360  }
89361 }
89362 MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut)
89363 {
89364  ma_uint64 framesRead;
89365  ma_uint32 unusedBitsPerSample;
89366  if (pFlac == NULL || framesToRead == 0) {
89367  return 0;
89368  }
89369  if (pBufferOut == NULL) {
89370  return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
89371  }
89372  MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
89373  unusedBitsPerSample = 32 - pFlac->bitsPerSample;
89374  framesRead = 0;
89375  while (framesToRead > 0) {
89376  if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
89377  if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
89378  break;
89379  }
89380  } else {
89381  unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
89382  ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
89383  ma_uint64 frameCountThisIteration = framesToRead;
89384  if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
89385  frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
89386  }
89387  if (channelCount == 2) {
89388  const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
89389  const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
89390  switch (pFlac->currentFLACFrame.header.channelAssignment)
89391  {
89392  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
89393  {
89394  ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
89395  } break;
89396  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
89397  {
89398  ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
89399  } break;
89400  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
89401  {
89402  ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
89403  } break;
89404  case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
89405  default:
89406  {
89407  ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
89408  } break;
89409  }
89410  } else {
89411  ma_uint64 i;
89412  for (i = 0; i < frameCountThisIteration; ++i) {
89413  unsigned int j;
89414  for (j = 0; j < channelCount; ++j) {
89415  ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
89416  pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0);
89417  }
89418  }
89419  }
89420  framesRead += frameCountThisIteration;
89421  pBufferOut += frameCountThisIteration * channelCount;
89422  framesToRead -= frameCountThisIteration;
89423  pFlac->currentPCMFrame += frameCountThisIteration;
89424  pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;
89425  }
89426  }
89427  return framesRead;
89428 }
89429 MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
89430 {
89431  if (pFlac == NULL) {
89432  return MA_FALSE;
89433  }
89434  if (pFlac->currentPCMFrame == pcmFrameIndex) {
89435  return MA_TRUE;
89436  }
89437  if (pFlac->firstFLACFramePosInBytes == 0) {
89438  return MA_FALSE;
89439  }
89440  if (pcmFrameIndex == 0) {
89441  pFlac->currentPCMFrame = 0;
89442  return ma_dr_flac__seek_to_first_frame(pFlac);
89443  } else {
89444  ma_bool32 wasSuccessful = MA_FALSE;
89445  ma_uint64 originalPCMFrame = pFlac->currentPCMFrame;
89446  if (pcmFrameIndex > pFlac->totalPCMFrameCount) {
89447  pcmFrameIndex = pFlac->totalPCMFrameCount;
89448  }
89449  if (pcmFrameIndex > pFlac->currentPCMFrame) {
89450  ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
89451  if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
89452  pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
89453  pFlac->currentPCMFrame = pcmFrameIndex;
89454  return MA_TRUE;
89455  }
89456  } else {
89457  ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
89458  ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
89459  ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
89460  if (currentFLACFramePCMFramesConsumed > offsetAbs) {
89461  pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
89462  pFlac->currentPCMFrame = pcmFrameIndex;
89463  return MA_TRUE;
89464  }
89465  }
89466 #ifndef MA_DR_FLAC_NO_OGG
89467  if (pFlac->container == ma_dr_flac_container_ogg)
89468  {
89469  wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex);
89470  }
89471  else
89472 #endif
89473  {
89474  if (!pFlac->_noSeekTableSeek) {
89475  wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);
89476  }
89477 #if !defined(MA_DR_FLAC_NO_CRC)
89478  if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {
89479  wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);
89480  }
89481 #endif
89482  if (!wasSuccessful && !pFlac->_noBruteForceSeek) {
89483  wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);
89484  }
89485  }
89486  if (wasSuccessful) {
89487  pFlac->currentPCMFrame = pcmFrameIndex;
89488  } else {
89489  if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) {
89490  ma_dr_flac_seek_to_pcm_frame(pFlac, 0);
89491  }
89492  }
89493  return wasSuccessful;
89494  }
89495 }
89496 #define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
89497 static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\
89498 { \
89499  type* pSampleData = NULL; \
89500  ma_uint64 totalPCMFrameCount; \
89501  \
89502  MA_DR_FLAC_ASSERT(pFlac != NULL); \
89503  \
89504  totalPCMFrameCount = pFlac->totalPCMFrameCount; \
89505  \
89506  if (totalPCMFrameCount == 0) { \
89507  type buffer[4096]; \
89508  ma_uint64 pcmFramesRead; \
89509  size_t sampleDataBufferSize = sizeof(buffer); \
89510  \
89511  pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
89512  if (pSampleData == NULL) { \
89513  goto on_error; \
89514  } \
89515  \
89516  while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
89517  if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
89518  type* pNewSampleData; \
89519  size_t newSampleDataBufferSize; \
89520  \
89521  newSampleDataBufferSize = sampleDataBufferSize * 2; \
89522  pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
89523  if (pNewSampleData == NULL) { \
89524  ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
89525  goto on_error; \
89526  } \
89527  \
89528  sampleDataBufferSize = newSampleDataBufferSize; \
89529  pSampleData = pNewSampleData; \
89530  } \
89531  \
89532  MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
89533  totalPCMFrameCount += pcmFramesRead; \
89534  } \
89535  \
89536  \
89537  MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
89538  } else { \
89539  ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
89540  if (dataSize > (ma_uint64)MA_SIZE_MAX) { \
89541  goto on_error; \
89542  } \
89543  \
89544  pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \
89545  if (pSampleData == NULL) { \
89546  goto on_error; \
89547  } \
89548  \
89549  totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
89550  } \
89551  \
89552  if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
89553  if (channelsOut) *channelsOut = pFlac->channels; \
89554  if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
89555  \
89556  ma_dr_flac_close(pFlac); \
89557  return pSampleData; \
89558  \
89559 on_error: \
89560  ma_dr_flac_close(pFlac); \
89561  return NULL; \
89562 }
89563 MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32)
89564 MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16)
89565 MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
89566 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)
89567 {
89568  ma_dr_flac* pFlac;
89569  if (channelsOut) {
89570  *channelsOut = 0;
89571  }
89572  if (sampleRateOut) {
89573  *sampleRateOut = 0;
89574  }
89575  if (totalPCMFrameCountOut) {
89576  *totalPCMFrameCountOut = 0;
89577  }
89578  pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
89579  if (pFlac == NULL) {
89580  return NULL;
89581  }
89582  return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
89583 }
89584 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)
89585 {
89586  ma_dr_flac* pFlac;
89587  if (channelsOut) {
89588  *channelsOut = 0;
89589  }
89590  if (sampleRateOut) {
89591  *sampleRateOut = 0;
89592  }
89593  if (totalPCMFrameCountOut) {
89594  *totalPCMFrameCountOut = 0;
89595  }
89596  pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
89597  if (pFlac == NULL) {
89598  return NULL;
89599  }
89600  return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
89601 }
89602 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)
89603 {
89604  ma_dr_flac* pFlac;
89605  if (channelsOut) {
89606  *channelsOut = 0;
89607  }
89608  if (sampleRateOut) {
89609  *sampleRateOut = 0;
89610  }
89611  if (totalPCMFrameCountOut) {
89612  *totalPCMFrameCountOut = 0;
89613  }
89614  pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
89615  if (pFlac == NULL) {
89616  return NULL;
89617  }
89618  return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
89619 }
89620 #ifndef MA_DR_FLAC_NO_STDIO
89621 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)
89622 {
89623  ma_dr_flac* pFlac;
89624  if (sampleRate) {
89625  *sampleRate = 0;
89626  }
89627  if (channels) {
89628  *channels = 0;
89629  }
89630  if (totalPCMFrameCount) {
89631  *totalPCMFrameCount = 0;
89632  }
89633  pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
89634  if (pFlac == NULL) {
89635  return NULL;
89636  }
89637  return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
89638 }
89639 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)
89640 {
89641  ma_dr_flac* pFlac;
89642  if (sampleRate) {
89643  *sampleRate = 0;
89644  }
89645  if (channels) {
89646  *channels = 0;
89647  }
89648  if (totalPCMFrameCount) {
89649  *totalPCMFrameCount = 0;
89650  }
89651  pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
89652  if (pFlac == NULL) {
89653  return NULL;
89654  }
89655  return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
89656 }
89657 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)
89658 {
89659  ma_dr_flac* pFlac;
89660  if (sampleRate) {
89661  *sampleRate = 0;
89662  }
89663  if (channels) {
89664  *channels = 0;
89665  }
89666  if (totalPCMFrameCount) {
89667  *totalPCMFrameCount = 0;
89668  }
89669  pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
89670  if (pFlac == NULL) {
89671  return NULL;
89672  }
89673  return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
89674 }
89675 #endif
89676 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)
89677 {
89678  ma_dr_flac* pFlac;
89679  if (sampleRate) {
89680  *sampleRate = 0;
89681  }
89682  if (channels) {
89683  *channels = 0;
89684  }
89685  if (totalPCMFrameCount) {
89686  *totalPCMFrameCount = 0;
89687  }
89688  pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
89689  if (pFlac == NULL) {
89690  return NULL;
89691  }
89692  return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
89693 }
89694 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)
89695 {
89696  ma_dr_flac* pFlac;
89697  if (sampleRate) {
89698  *sampleRate = 0;
89699  }
89700  if (channels) {
89701  *channels = 0;
89702  }
89703  if (totalPCMFrameCount) {
89704  *totalPCMFrameCount = 0;
89705  }
89706  pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
89707  if (pFlac == NULL) {
89708  return NULL;
89709  }
89710  return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
89711 }
89712 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)
89713 {
89714  ma_dr_flac* pFlac;
89715  if (sampleRate) {
89716  *sampleRate = 0;
89717  }
89718  if (channels) {
89719  *channels = 0;
89720  }
89721  if (totalPCMFrameCount) {
89722  *totalPCMFrameCount = 0;
89723  }
89724  pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
89725  if (pFlac == NULL) {
89726  return NULL;
89727  }
89728  return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
89729 }
89730 MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
89731 {
89732  if (pAllocationCallbacks != NULL) {
89733  ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks);
89734  } else {
89735  ma_dr_flac__free_default(p, NULL);
89736  }
89737 }
89738 MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments)
89739 {
89740  if (pIter == NULL) {
89741  return;
89742  }
89743  pIter->countRemaining = commentCount;
89744  pIter->pRunningData = (const char*)pComments;
89745 }
89746 MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut)
89747 {
89748  ma_int32 length;
89749  const char* pComment;
89750  if (pCommentLengthOut) {
89751  *pCommentLengthOut = 0;
89752  }
89753  if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
89754  return NULL;
89755  }
89756  length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData);
89757  pIter->pRunningData += 4;
89758  pComment = pIter->pRunningData;
89759  pIter->pRunningData += length;
89760  pIter->countRemaining -= 1;
89761  if (pCommentLengthOut) {
89762  *pCommentLengthOut = length;
89763  }
89764  return pComment;
89765 }
89766 MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData)
89767 {
89768  if (pIter == NULL) {
89769  return;
89770  }
89771  pIter->countRemaining = trackCount;
89772  pIter->pRunningData = (const char*)pTrackData;
89773 }
89774 MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack)
89775 {
89776  ma_dr_flac_cuesheet_track cuesheetTrack;
89777  const char* pRunningData;
89778  ma_uint64 offsetHi;
89779  ma_uint64 offsetLo;
89780  if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
89781  return MA_FALSE;
89782  }
89783  pRunningData = pIter->pRunningData;
89784  offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4;
89785  offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4;
89786  cuesheetTrack.offset = offsetLo | (offsetHi << 32);
89787  cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
89788  MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
89789  cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
89790  cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
89791  cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
89792  cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index);
89793  pIter->pRunningData = pRunningData;
89794  pIter->countRemaining -= 1;
89795  if (pCuesheetTrack) {
89796  *pCuesheetTrack = cuesheetTrack;
89797  }
89798  return MA_TRUE;
89799 }
89800 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
89801  #pragma GCC diagnostic pop
89802 #endif
89803 #endif
89804 /* dr_flac_c end */
89805 #endif /* MA_DR_FLAC_IMPLEMENTATION */
89806 #endif /* MA_NO_FLAC */
89807 
89808 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
89809 #if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
89810 /* dr_mp3_c begin */
89811 #ifndef ma_dr_mp3_c
89812 #define ma_dr_mp3_c
89813 #include <stdlib.h>
89814 #include <string.h>
89815 #include <limits.h>
89816 MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
89817 {
89818  if (pMajor) {
89819  *pMajor = MA_DR_MP3_VERSION_MAJOR;
89820  }
89821  if (pMinor) {
89822  *pMinor = MA_DR_MP3_VERSION_MINOR;
89823  }
89824  if (pRevision) {
89825  *pRevision = MA_DR_MP3_VERSION_REVISION;
89826  }
89827 }
89828 MA_API const char* ma_dr_mp3_version_string(void)
89829 {
89830  return MA_DR_MP3_VERSION_STRING;
89831 }
89832 #if defined(__TINYC__)
89833 #define MA_DR_MP3_NO_SIMD
89834 #endif
89835 #define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset)))
89836 #define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304
89837 #ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES
89838 #define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10
89839 #endif
89840 #define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE
89841 #define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511
89842 #define MA_DR_MP3_SHORT_BLOCK_TYPE 2
89843 #define MA_DR_MP3_STOP_BLOCK_TYPE 3
89844 #define MA_DR_MP3_MODE_MONO 3
89845 #define MA_DR_MP3_MODE_JOINT_STEREO 1
89846 #define MA_DR_MP3_HDR_SIZE 4
89847 #define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0)
89848 #define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60)
89849 #define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0)
89850 #define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1))
89851 #define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2)
89852 #define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8)
89853 #define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10)
89854 #define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10)
89855 #define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20)
89856 #define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3)
89857 #define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3)
89858 #define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3)
89859 #define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4)
89860 #define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3)
89861 #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)
89862 #define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2)
89863 #define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6)
89864 #define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1
89865 #define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210)
89866 #define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3)
89867 #define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a))
89868 #define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a))
89869 #if !defined(MA_DR_MP3_NO_SIMD)
89870 #if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
89871 #define MA_DR_MP3_ONLY_SIMD
89872 #endif
89873 #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__)))
89874 #if defined(_MSC_VER)
89875 #include <intrin.h>
89876 #endif
89877 #include <emmintrin.h>
89878 #define MA_DR_MP3_HAVE_SSE 1
89879 #define MA_DR_MP3_HAVE_SIMD 1
89880 #define MA_DR_MP3_VSTORE _mm_storeu_ps
89881 #define MA_DR_MP3_VLD _mm_loadu_ps
89882 #define MA_DR_MP3_VSET _mm_set1_ps
89883 #define MA_DR_MP3_VADD _mm_add_ps
89884 #define MA_DR_MP3_VSUB _mm_sub_ps
89885 #define MA_DR_MP3_VMUL _mm_mul_ps
89886 #define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))
89887 #define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))
89888 #define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
89889 #define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
89890 typedef __m128 ma_dr_mp3_f4;
89891 #if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD)
89892 #define ma_dr_mp3_cpuid __cpuid
89893 #else
89894 static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType)
89895 {
89896 #if defined(__PIC__)
89897  __asm__ __volatile__(
89898 #if defined(__x86_64__)
89899  "push %%rbx\n"
89900  "cpuid\n"
89901  "xchgl %%ebx, %1\n"
89902  "pop %%rbx\n"
89903 #else
89904  "xchgl %%ebx, %1\n"
89905  "cpuid\n"
89906  "xchgl %%ebx, %1\n"
89907 #endif
89908  : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
89909  : "a" (InfoType));
89910 #else
89911  __asm__ __volatile__(
89912  "cpuid"
89913  : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
89914  : "a" (InfoType));
89915 #endif
89916 }
89917 #endif
89918 static int ma_dr_mp3_have_simd(void)
89919 {
89920 #ifdef MA_DR_MP3_ONLY_SIMD
89921  return 1;
89922 #else
89923  static int g_have_simd;
89924  int CPUInfo[4];
89925 #ifdef MINIMP3_TEST
89926  static int g_counter;
89927  if (g_counter++ > 100)
89928  return 0;
89929 #endif
89930  if (g_have_simd)
89931  goto end;
89932  ma_dr_mp3_cpuid(CPUInfo, 0);
89933  if (CPUInfo[0] > 0)
89934  {
89935  ma_dr_mp3_cpuid(CPUInfo, 1);
89936  g_have_simd = (CPUInfo[3] & (1 << 26)) + 1;
89937  return g_have_simd - 1;
89938  }
89939 end:
89940  return g_have_simd - 1;
89941 #endif
89942 }
89943 #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
89944 #include <arm_neon.h>
89945 #define MA_DR_MP3_HAVE_SSE 0
89946 #define MA_DR_MP3_HAVE_SIMD 1
89947 #define MA_DR_MP3_VSTORE vst1q_f32
89948 #define MA_DR_MP3_VLD vld1q_f32
89949 #define MA_DR_MP3_VSET vmovq_n_f32
89950 #define MA_DR_MP3_VADD vaddq_f32
89951 #define MA_DR_MP3_VSUB vsubq_f32
89952 #define MA_DR_MP3_VMUL vmulq_f32
89953 #define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y)
89954 #define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y)
89955 #define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
89956 #define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
89957 typedef float32x4_t ma_dr_mp3_f4;
89958 static int ma_dr_mp3_have_simd(void)
89959 {
89960  return 1;
89961 }
89962 #else
89963 #define MA_DR_MP3_HAVE_SSE 0
89964 #define MA_DR_MP3_HAVE_SIMD 0
89965 #ifdef MA_DR_MP3_ONLY_SIMD
89966 #error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
89967 #endif
89968 #endif
89969 #else
89970 #define MA_DR_MP3_HAVE_SIMD 0
89971 #endif
89972 #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__)
89973 #define MA_DR_MP3_HAVE_ARMV6 1
89974 static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a)
89975 {
89976  ma_int32 x = 0;
89977  __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
89978  return x;
89979 }
89980 #else
89981 #define MA_DR_MP3_HAVE_ARMV6 0
89982 #endif
89983 #ifndef MA_DR_MP3_ASSERT
89984 #include <assert.h>
89985 #define MA_DR_MP3_ASSERT(expression) assert(expression)
89986 #endif
89987 #ifndef MA_DR_MP3_COPY_MEMORY
89988 #define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
89989 #endif
89990 #ifndef MA_DR_MP3_MOVE_MEMORY
89991 #define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
89992 #endif
89993 #ifndef MA_DR_MP3_ZERO_MEMORY
89994 #define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
89995 #endif
89996 #define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p)))
89997 #ifndef MA_DR_MP3_MALLOC
89998 #define MA_DR_MP3_MALLOC(sz) malloc((sz))
89999 #endif
90000 #ifndef MA_DR_MP3_REALLOC
90001 #define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz))
90002 #endif
90003 #ifndef MA_DR_MP3_FREE
90004 #define MA_DR_MP3_FREE(p) free((p))
90005 #endif
90006 typedef struct
90007 {
90008  const ma_uint8 *buf;
90009  int pos, limit;
90010 } ma_dr_mp3_bs;
90011 typedef struct
90012 {
90013  float scf[3*64];
90014  ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
90015 } ma_dr_mp3_L12_scale_info;
90016 typedef struct
90017 {
90018  ma_uint8 tab_offset, code_tab_width, band_count;
90019 } ma_dr_mp3_L12_subband_alloc;
90020 typedef struct
90021 {
90022  const ma_uint8 *sfbtab;
90023  ma_uint16 part_23_length, big_values, scalefac_compress;
90024  ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
90025  ma_uint8 table_select[3], region_count[3], subblock_gain[3];
90026  ma_uint8 preflag, scalefac_scale, count1_table, scfsi;
90027 } ma_dr_mp3_L3_gr_info;
90028 typedef struct
90029 {
90030  ma_dr_mp3_bs bs;
90031  ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES];
90032  ma_dr_mp3_L3_gr_info gr_info[4];
90033  float grbuf[2][576], scf[40], syn[18 + 15][2*32];
90034  ma_uint8 ist_pos[2][39];
90035 } ma_dr_mp3dec_scratch;
90036 static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes)
90037 {
90038  bs->buf = data;
90039  bs->pos = 0;
90040  bs->limit = bytes*8;
90041 }
90042 static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n)
90043 {
90044  ma_uint32 next, cache = 0, s = bs->pos & 7;
90045  int shl = n + s;
90046  const ma_uint8 *p = bs->buf + (bs->pos >> 3);
90047  if ((bs->pos += n) > bs->limit)
90048  return 0;
90049  next = *p++ & (255 >> s);
90050  while ((shl -= 8) > 0)
90051  {
90052  cache |= next << shl;
90053  next = *p++;
90054  }
90055  return cache | (next >> -shl);
90056 }
90057 static int ma_dr_mp3_hdr_valid(const ma_uint8 *h)
90058 {
90059  return h[0] == 0xff &&
90060  ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
90061  (MA_DR_MP3_HDR_GET_LAYER(h) != 0) &&
90062  (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) &&
90063  (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3);
90064 }
90065 static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2)
90066 {
90067  return ma_dr_mp3_hdr_valid(h2) &&
90068  ((h1[1] ^ h2[1]) & 0xFE) == 0 &&
90069  ((h1[2] ^ h2[2]) & 0x0C) == 0 &&
90070  !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2));
90071 }
90072 static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h)
90073 {
90074  static const ma_uint8 halfrate[2][3][15] = {
90075  { { 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 } },
90076  { { 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 } },
90077  };
90078  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)];
90079 }
90080 static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h)
90081 {
90082  static const unsigned g_hz[3] = { 44100, 48000, 32000 };
90083  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);
90084 }
90085 static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h)
90086 {
90087  return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h));
90088 }
90089 static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size)
90090 {
90091  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);
90092  if (MA_DR_MP3_HDR_IS_LAYER_1(h))
90093  {
90094  frame_bytes &= ~3;
90095  }
90096  return frame_bytes ? frame_bytes : free_format_size;
90097 }
90098 static int ma_dr_mp3_hdr_padding(const ma_uint8 *h)
90099 {
90100  return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;
90101 }
90102 #ifndef MA_DR_MP3_ONLY_MP3
90103 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)
90104 {
90105  const ma_dr_mp3_L12_subband_alloc *alloc;
90106  int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr);
90107  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;
90108  if (MA_DR_MP3_HDR_IS_LAYER_1(hdr))
90109  {
90110  static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };
90111  alloc = g_alloc_L1;
90112  nbands = 32;
90113  } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr))
90114  {
90115  static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };
90116  alloc = g_alloc_L2M2;
90117  nbands = 30;
90118  } else
90119  {
90120  static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };
90121  int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr);
90122  unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO);
90123  if (!kbps)
90124  {
90125  kbps = 192;
90126  }
90127  alloc = g_alloc_L2M1;
90128  nbands = 27;
90129  if (kbps < 56)
90130  {
90131  static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };
90132  alloc = g_alloc_L2M1_lowrate;
90133  nbands = sample_rate_idx == 2 ? 12 : 8;
90134  } else if (kbps >= 96 && sample_rate_idx != 1)
90135  {
90136  nbands = 30;
90137  }
90138  }
90139  sci->total_bands = (ma_uint8)nbands;
90140  sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands);
90141  return alloc;
90142 }
90143 static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf)
90144 {
90145  static const float g_deq_L12[18*3] = {
90146 #define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x
90147  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)
90148  };
90149  int i, m;
90150  for (i = 0; i < bands; i++)
90151  {
90152  float s = 0;
90153  int ba = *pba++;
90154  int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;
90155  for (m = 4; m; m >>= 1)
90156  {
90157  if (mask & m)
90158  {
90159  int b = ma_dr_mp3_bs_get_bits(bs, 6);
90160  s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);
90161  }
90162  *scf++ = s;
90163  }
90164  }
90165 }
90166 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)
90167 {
90168  static const ma_uint8 g_bitalloc_code_tab[] = {
90169  0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,
90170  0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,
90171  0,17,18, 3,19,4,5,16,
90172  0,17,18,16,
90173  0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,
90174  0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,
90175  0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16
90176  };
90177  const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci);
90178  int i, k = 0, ba_bits = 0;
90179  const ma_uint8 *ba_code_tab = g_bitalloc_code_tab;
90180  for (i = 0; i < sci->total_bands; i++)
90181  {
90182  ma_uint8 ba;
90183  if (i == k)
90184  {
90185  k += subband_alloc->band_count;
90186  ba_bits = subband_alloc->code_tab_width;
90187  ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;
90188  subband_alloc++;
90189  }
90190  ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)];
90191  sci->bitalloc[2*i] = ba;
90192  if (i < sci->stereo_bands)
90193  {
90194  ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)];
90195  }
90196  sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;
90197  }
90198  for (i = 0; i < 2*sci->total_bands; i++)
90199  {
90200  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);
90201  }
90202  ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);
90203  for (i = sci->stereo_bands; i < sci->total_bands; i++)
90204  {
90205  sci->bitalloc[2*i + 1] = 0;
90206  }
90207 }
90208 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)
90209 {
90210  int i, j, k, choff = 576;
90211  for (j = 0; j < 4; j++)
90212  {
90213  float *dst = grbuf + group_size*j;
90214  for (i = 0; i < 2*sci->total_bands; i++)
90215  {
90216  int ba = sci->bitalloc[i];
90217  if (ba != 0)
90218  {
90219  if (ba < 17)
90220  {
90221  int half = (1 << (ba - 1)) - 1;
90222  for (k = 0; k < group_size; k++)
90223  {
90224  dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half);
90225  }
90226  } else
90227  {
90228  unsigned mod = (2 << (ba - 17)) + 1;
90229  unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3));
90230  for (k = 0; k < group_size; k++, code /= mod)
90231  {
90232  dst[k] = (float)((int)(code % mod - mod/2));
90233  }
90234  }
90235  }
90236  dst += choff;
90237  choff = 18 - choff;
90238  }
90239  }
90240  return group_size*4;
90241 }
90242 static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst)
90243 {
90244  int i, k;
90245  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));
90246  for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
90247  {
90248  for (k = 0; k < 12; k++)
90249  {
90250  dst[k + 0] *= scf[0];
90251  dst[k + 576] *= scf[3];
90252  }
90253  }
90254 }
90255 #endif
90256 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)
90257 {
90258  static const ma_uint8 g_scf_long[8][23] = {
90259  { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
90260  { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },
90261  { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
90262  { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },
90263  { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
90264  { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },
90265  { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },
90266  { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }
90267  };
90268  static const ma_uint8 g_scf_short[8][40] = {
90269  { 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 },
90270  { 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 },
90271  { 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 },
90272  { 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 },
90273  { 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 },
90274  { 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 },
90275  { 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 },
90276  { 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 }
90277  };
90278  static const ma_uint8 g_scf_mixed[8][40] = {
90279  { 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 },
90280  { 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 },
90281  { 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 },
90282  { 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 },
90283  { 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 },
90284  { 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 },
90285  { 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 },
90286  { 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 }
90287  };
90288  unsigned tables, scfsi = 0;
90289  int main_data_begin, part_23_sum = 0;
90290  int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2;
90291  int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
90292  if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
90293  {
90294  gr_count *= 2;
90295  main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9);
90296  scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count);
90297  } else
90298  {
90299  main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;
90300  }
90301  do
90302  {
90303  if (MA_DR_MP3_HDR_IS_MONO(hdr))
90304  {
90305  scfsi <<= 4;
90306  }
90307  gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12);
90308  part_23_sum += gr->part_23_length;
90309  gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9);
90310  if (gr->big_values > 288)
90311  {
90312  return -1;
90313  }
90314  gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8);
90315  gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);
90316  gr->sfbtab = g_scf_long[sr_idx];
90317  gr->n_long_sfb = 22;
90318  gr->n_short_sfb = 0;
90319  if (ma_dr_mp3_bs_get_bits(bs, 1))
90320  {
90321  gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2);
90322  if (!gr->block_type)
90323  {
90324  return -1;
90325  }
90326  gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
90327  gr->region_count[0] = 7;
90328  gr->region_count[1] = 255;
90329  if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE)
90330  {
90331  scfsi &= 0x0F0F;
90332  if (!gr->mixed_block_flag)
90333  {
90334  gr->region_count[0] = 8;
90335  gr->sfbtab = g_scf_short[sr_idx];
90336  gr->n_long_sfb = 0;
90337  gr->n_short_sfb = 39;
90338  } else
90339  {
90340  gr->sfbtab = g_scf_mixed[sr_idx];
90341  gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;
90342  gr->n_short_sfb = 30;
90343  }
90344  }
90345  tables = ma_dr_mp3_bs_get_bits(bs, 10);
90346  tables <<= 5;
90347  gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
90348  gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
90349  gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
90350  } else
90351  {
90352  gr->block_type = 0;
90353  gr->mixed_block_flag = 0;
90354  tables = ma_dr_mp3_bs_get_bits(bs, 15);
90355  gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4);
90356  gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
90357  gr->region_count[2] = 255;
90358  }
90359  gr->table_select[0] = (ma_uint8)(tables >> 10);
90360  gr->table_select[1] = (ma_uint8)((tables >> 5) & 31);
90361  gr->table_select[2] = (ma_uint8)((tables) & 31);
90362  gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));
90363  gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
90364  gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
90365  gr->scfsi = (ma_uint8)((scfsi >> 12) & 15);
90366  scfsi <<= 4;
90367  gr++;
90368  } while(--gr_count);
90369  if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)
90370  {
90371  return -1;
90372  }
90373  return main_data_begin;
90374 }
90375 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)
90376 {
90377  int i, k;
90378  for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)
90379  {
90380  int cnt = scf_count[i];
90381  if (scfsi & 8)
90382  {
90383  MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt);
90384  } else
90385  {
90386  int bits = scf_size[i];
90387  if (!bits)
90388  {
90389  MA_DR_MP3_ZERO_MEMORY(scf, cnt);
90390  MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt);
90391  } else
90392  {
90393  int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
90394  for (k = 0; k < cnt; k++)
90395  {
90396  int s = ma_dr_mp3_bs_get_bits(bitbuf, bits);
90397  ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s);
90398  scf[k] = (ma_uint8)s;
90399  }
90400  }
90401  }
90402  ist_pos += cnt;
90403  scf += cnt;
90404  }
90405  scf[0] = scf[1] = scf[2] = 0;
90406 }
90407 static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2)
90408 {
90409  static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };
90410  int e;
90411  do
90412  {
90413  e = MA_DR_MP3_MIN(30*4, exp_q2);
90414  y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));
90415  } while ((exp_q2 -= e) > 0);
90416  return y;
90417 }
90418 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)
90419 {
90420  static const ma_uint8 g_scf_partitions[3][28] = {
90421  { 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 },
90422  { 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 },
90423  { 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 }
90424  };
90425  const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];
90426  ma_uint8 scf_size[4], iscf[40];
90427  int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;
90428  float gain;
90429  if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
90430  {
90431  static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };
90432  int part = g_scfc_decode[gr->scalefac_compress];
90433  scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2);
90434  scf_size[3] = scf_size[2] = (ma_uint8)(part & 3);
90435  } else
90436  {
90437  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 };
90438  int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch;
90439  sfc = gr->scalefac_compress >> ist;
90440  for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)
90441  {
90442  for (modprod = 1, i = 3; i >= 0; i--)
90443  {
90444  scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]);
90445  modprod *= g_mod[k + i];
90446  }
90447  }
90448  scf_partition += k;
90449  scfsi = -16;
90450  }
90451  ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);
90452  if (gr->n_short_sfb)
90453  {
90454  int sh = 3 - scf_shift;
90455  for (i = 0; i < gr->n_short_sfb; i += 3)
90456  {
90457  iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
90458  iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
90459  iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
90460  }
90461  } else if (gr->preflag)
90462  {
90463  static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
90464  for (i = 0; i < 10; i++)
90465  {
90466  iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]);
90467  }
90468  }
90469  gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
90470  gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp);
90471  for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
90472  {
90473  scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
90474  }
90475 }
90476 static const float g_ma_dr_mp3_pow43[129 + 16] = {
90477  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,
90478  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
90479 };
90480 static float ma_dr_mp3_L3_pow_43(int x)
90481 {
90482  float frac;
90483  int sign, mult = 256;
90484  if (x < 129)
90485  {
90486  return g_ma_dr_mp3_pow43[16 + x];
90487  }
90488  if (x < 1024)
90489  {
90490  mult = 16;
90491  x <<= 3;
90492  }
90493  sign = 2*x & 64;
90494  frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
90495  return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
90496 }
90497 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)
90498 {
90499  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,
90500  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,
90501  -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,
90502  -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,
90503  -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,
90504  -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,
90505  -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,
90506  -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,
90507  -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,
90508  -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,
90509  -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,
90510  -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,
90511  -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,
90512  -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,
90513  -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,
90514  -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 };
90515  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};
90516  static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
90517  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 };
90518  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 };
90519 #define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n)))
90520 #define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
90521 #define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
90522 #define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
90523  float one = 0.0f;
90524  int ireg = 0, big_val_cnt = gr_info->big_values;
90525  const ma_uint8 *sfb = gr_info->sfbtab;
90526  const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
90527  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);
90528  int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
90529  bs_next_ptr += 4;
90530  while (big_val_cnt > 0)
90531  {
90532  int tab_num = gr_info->table_select[ireg];
90533  int sfb_cnt = gr_info->region_count[ireg++];
90534  const ma_int16 *codebook = tabs + tabindex[tab_num];
90535  int linbits = g_linbits[tab_num];
90536  if (linbits)
90537  {
90538  do
90539  {
90540  np = *sfb++ / 2;
90541  pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np);
90542  one = *scf++;
90543  do
90544  {
90545  int j, w = 5;
90546  int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)];
90547  while (leaf < 0)
90548  {
90549  MA_DR_MP3_FLUSH_BITS(w);
90550  w = leaf & 7;
90551  leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)];
90552  }
90553  MA_DR_MP3_FLUSH_BITS(leaf >> 8);
90554  for (j = 0; j < 2; j++, dst++, leaf >>= 4)
90555  {
90556  int lsb = leaf & 0x0F;
90557  if (lsb == 15)
90558  {
90559  lsb += MA_DR_MP3_PEEK_BITS(linbits);
90560  MA_DR_MP3_FLUSH_BITS(linbits);
90561  MA_DR_MP3_CHECK_BITS;
90562  *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1);
90563  } else
90564  {
90565  *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
90566  }
90567  MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0);
90568  }
90569  MA_DR_MP3_CHECK_BITS;
90570  } while (--pairs_to_decode);
90571  } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
90572  } else
90573  {
90574  do
90575  {
90576  np = *sfb++ / 2;
90577  pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np);
90578  one = *scf++;
90579  do
90580  {
90581  int j, w = 5;
90582  int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)];
90583  while (leaf < 0)
90584  {
90585  MA_DR_MP3_FLUSH_BITS(w);
90586  w = leaf & 7;
90587  leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)];
90588  }
90589  MA_DR_MP3_FLUSH_BITS(leaf >> 8);
90590  for (j = 0; j < 2; j++, dst++, leaf >>= 4)
90591  {
90592  int lsb = leaf & 0x0F;
90593  *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
90594  MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0);
90595  }
90596  MA_DR_MP3_CHECK_BITS;
90597  } while (--pairs_to_decode);
90598  } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
90599  }
90600  }
90601  for (np = 1 - big_val_cnt;; dst += 4)
90602  {
90603  const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;
90604  int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)];
90605  if (!(leaf & 8))
90606  {
90607  leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];
90608  }
90609  MA_DR_MP3_FLUSH_BITS(leaf & 7);
90610  if (MA_DR_MP3_BSPOS > layer3gr_limit)
90611  {
90612  break;
90613  }
90614 #define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }
90615 #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) }
90616  MA_DR_MP3_RELOAD_SCALEFACTOR;
90617  MA_DR_MP3_DEQ_COUNT1(0);
90618  MA_DR_MP3_DEQ_COUNT1(1);
90619  MA_DR_MP3_RELOAD_SCALEFACTOR;
90620  MA_DR_MP3_DEQ_COUNT1(2);
90621  MA_DR_MP3_DEQ_COUNT1(3);
90622  MA_DR_MP3_CHECK_BITS;
90623  }
90624  bs->pos = layer3gr_limit;
90625 }
90626 static void ma_dr_mp3_L3_midside_stereo(float *left, int n)
90627 {
90628  int i = 0;
90629  float *right = left + 576;
90630 #if MA_DR_MP3_HAVE_SIMD
90631  if (ma_dr_mp3_have_simd())
90632  {
90633  for (; i < n - 3; i += 4)
90634  {
90635  ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i);
90636  ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i);
90637  MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr));
90638  MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr));
90639  }
90640 #ifdef __GNUC__
90641  if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)
90642  return;
90643 #endif
90644  }
90645 #endif
90646  for (; i < n; i++)
90647  {
90648  float a = left[i];
90649  float b = right[i];
90650  left[i] = a + b;
90651  right[i] = a - b;
90652  }
90653 }
90654 static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)
90655 {
90656  int i;
90657  for (i = 0; i < n; i++)
90658  {
90659  left[i + 576] = left[i]*kr;
90660  left[i] = left[i]*kl;
90661  }
90662 }
90663 static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3])
90664 {
90665  int i, k;
90666  max_band[0] = max_band[1] = max_band[2] = -1;
90667  for (i = 0; i < nbands; i++)
90668  {
90669  for (k = 0; k < sfb[i]; k += 2)
90670  {
90671  if (right[k] != 0 || right[k + 1] != 0)
90672  {
90673  max_band[i % 3] = i;
90674  break;
90675  }
90676  }
90677  right += sfb[i];
90678  }
90679 }
90680 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)
90681 {
90682  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 };
90683  unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;
90684  for (i = 0; sfb[i]; i++)
90685  {
90686  unsigned ipos = ist_pos[i];
90687  if ((int)i > max_band[i % 3] && ipos < max_pos)
90688  {
90689  float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
90690  if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
90691  {
90692  kl = g_pan[2*ipos];
90693  kr = g_pan[2*ipos + 1];
90694  } else
90695  {
90696  kl = 1;
90697  kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);
90698  if (ipos & 1)
90699  {
90700  kl = kr;
90701  kr = 1;
90702  }
90703  }
90704  ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);
90705  } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr))
90706  {
90707  ma_dr_mp3_L3_midside_stereo(left, sfb[i]);
90708  }
90709  left += sfb[i];
90710  }
90711 }
90712 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)
90713 {
90714  int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;
90715  int i, max_blocks = gr->n_short_sfb ? 3 : 1;
90716  ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);
90717  if (gr->n_long_sfb)
90718  {
90719  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]);
90720  }
90721  for (i = 0; i < max_blocks; i++)
90722  {
90723  int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;
90724  int itop = n_sfb - max_blocks + i;
90725  int prev = itop - max_blocks;
90726  ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);
90727  }
90728  ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);
90729 }
90730 static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb)
90731 {
90732  int i, len;
90733  float *src = grbuf, *dst = scratch;
90734  for (;0 != (len = *sfb); sfb += 3, src += 2*len)
90735  {
90736  for (i = 0; i < len; i++, src++)
90737  {
90738  *dst++ = src[0*len];
90739  *dst++ = src[1*len];
90740  *dst++ = src[2*len];
90741  }
90742  }
90743  MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float));
90744 }
90745 static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands)
90746 {
90747  static const float g_aa[2][8] = {
90748  {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},
90749  {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}
90750  };
90751  for (; nbands > 0; nbands--, grbuf += 18)
90752  {
90753  int i = 0;
90754 #if MA_DR_MP3_HAVE_SIMD
90755  if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4)
90756  {
90757  ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i);
90758  ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i);
90759  ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i);
90760  ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i);
90761  vd = MA_DR_MP3_VREV(vd);
90762  MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1)));
90763  vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0));
90764  MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd));
90765  }
90766 #endif
90767 #ifndef MA_DR_MP3_ONLY_SIMD
90768  for(; i < 8; i++)
90769  {
90770  float u = grbuf[18 + i];
90771  float d = grbuf[17 - i];
90772  grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];
90773  grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];
90774  }
90775 #endif
90776  }
90777 }
90778 static void ma_dr_mp3_L3_dct3_9(float *y)
90779 {
90780  float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;
90781  s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];
90782  t0 = s0 + s6*0.5f;
90783  s0 -= s6;
90784  t4 = (s4 + s2)*0.93969262f;
90785  t2 = (s8 + s2)*0.76604444f;
90786  s6 = (s4 - s8)*0.17364818f;
90787  s4 += s8 - s2;
90788  s2 = s0 - s4*0.5f;
90789  y[4] = s4 + s0;
90790  s8 = t0 - t2 + s6;
90791  s0 = t0 - t4 + t2;
90792  s4 = t0 + t4 - s6;
90793  s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];
90794  s3 *= 0.86602540f;
90795  t0 = (s5 + s1)*0.98480775f;
90796  t4 = (s5 - s7)*0.34202014f;
90797  t2 = (s1 + s7)*0.64278761f;
90798  s1 = (s1 - s5 - s7)*0.86602540f;
90799  s5 = t0 - s3 - t2;
90800  s7 = t4 - s3 - t0;
90801  s3 = t4 + s3 - t2;
90802  y[0] = s4 - s7;
90803  y[1] = s2 + s1;
90804  y[2] = s0 - s3;
90805  y[3] = s8 + s5;
90806  y[5] = s8 - s5;
90807  y[6] = s0 + s3;
90808  y[7] = s2 - s1;
90809  y[8] = s4 + s7;
90810 }
90811 static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)
90812 {
90813  int i, j;
90814  static const float g_twid9[18] = {
90815  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
90816  };
90817  for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)
90818  {
90819  float co[9], si[9];
90820  co[0] = -grbuf[0];
90821  si[0] = grbuf[17];
90822  for (i = 0; i < 4; i++)
90823  {
90824  si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2];
90825  co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2];
90826  si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3];
90827  co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);
90828  }
90829  ma_dr_mp3_L3_dct3_9(co);
90830  ma_dr_mp3_L3_dct3_9(si);
90831  si[1] = -si[1];
90832  si[3] = -si[3];
90833  si[5] = -si[5];
90834  si[7] = -si[7];
90835  i = 0;
90836 #if MA_DR_MP3_HAVE_SIMD
90837  if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4)
90838  {
90839  ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i);
90840  ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i);
90841  ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i);
90842  ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i);
90843  ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i);
90844  ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i);
90845  ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i);
90846  ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0));
90847  MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1)));
90848  MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1)));
90849  vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0));
90850  MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum));
90851  }
90852 #endif
90853  for (; i < 9; i++)
90854  {
90855  float ovl = overlap[i];
90856  float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];
90857  overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];
90858  grbuf[i] = ovl*window[0 + i] - sum*window[9 + i];
90859  grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];
90860  }
90861  }
90862 }
90863 static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst)
90864 {
90865  float m1 = x1*0.86602540f;
90866  float a1 = x0 - x2*0.5f;
90867  dst[1] = x0 + x2;
90868  dst[0] = a1 + m1;
90869  dst[2] = a1 - m1;
90870 }
90871 static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap)
90872 {
90873  static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };
90874  float co[3], si[3];
90875  int i;
90876  ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);
90877  ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);
90878  si[1] = -si[1];
90879  for (i = 0; i < 3; i++)
90880  {
90881  float ovl = overlap[i];
90882  float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];
90883  overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];
90884  dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];
90885  dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];
90886  }
90887 }
90888 static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
90889 {
90890  for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
90891  {
90892  float tmp[18];
90893  MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp));
90894  MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float));
90895  ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
90896  ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
90897  ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
90898  }
90899 }
90900 static void ma_dr_mp3_L3_change_sign(float *grbuf)
90901 {
90902  int b, i;
90903  for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)
90904  for (i = 1; i < 18; i += 2)
90905  grbuf[i] = -grbuf[i];
90906 }
90907 static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)
90908 {
90909  static const float g_mdct_window[2][18] = {
90910  { 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 },
90911  { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }
90912  };
90913  if (n_long_bands)
90914  {
90915  ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);
90916  grbuf += 18*n_long_bands;
90917  overlap += 9*n_long_bands;
90918  }
90919  if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE)
90920  ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);
90921  else
90922  ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands);
90923 }
90924 static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s)
90925 {
90926  int pos = (s->bs.pos + 7)/8u;
90927  int remains = s->bs.limit/8u - pos;
90928  if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES)
90929  {
90930  pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES;
90931  remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES;
90932  }
90933  if (remains > 0)
90934  {
90935  MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains);
90936  }
90937  h->reserv = remains;
90938 }
90939 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)
90940 {
90941  int frame_bytes = (bs->limit - bs->pos)/8;
90942  int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin);
90943  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));
90944  MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
90945  ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
90946  return h->reserv >= main_data_begin;
90947 }
90948 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)
90949 {
90950  int ch;
90951  for (ch = 0; ch < nch; ch++)
90952  {
90953  int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;
90954  ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);
90955  ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);
90956  }
90957  if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header))
90958  {
90959  ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);
90960  } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header))
90961  {
90962  ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576);
90963  }
90964  for (ch = 0; ch < nch; ch++, gr_info++)
90965  {
90966  int aa_bands = 31;
90967  int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);
90968  if (gr_info->n_short_sfb)
90969  {
90970  aa_bands = n_long_bands - 1;
90971  ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);
90972  }
90973  ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands);
90974  ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);
90975  ma_dr_mp3_L3_change_sign(s->grbuf[ch]);
90976  }
90977 }
90978 static void ma_dr_mp3d_DCT_II(float *grbuf, int n)
90979 {
90980  static const float g_sec[24] = {
90981  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
90982  };
90983  int i, k = 0;
90984 #if MA_DR_MP3_HAVE_SIMD
90985  if (ma_dr_mp3_have_simd()) for (; k < n; k += 4)
90986  {
90987  ma_dr_mp3_f4 t[4][8], *x;
90988  float *y = grbuf + k;
90989  for (x = t[0], i = 0; i < 8; i++, x++)
90990  {
90991  ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]);
90992  ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]);
90993  ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]);
90994  ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]);
90995  ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3);
90996  ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2);
90997  ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]);
90998  ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]);
90999  x[0] = MA_DR_MP3_VADD(t0, t1);
91000  x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]);
91001  x[16] = MA_DR_MP3_VADD(t3, t2);
91002  x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]);
91003  }
91004  for (x = t[0], i = 0; i < 4; i++, x += 8)
91005  {
91006  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;
91007  xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7);
91008  x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6);
91009  x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5);
91010  x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4);
91011  x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3);
91012  x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2);
91013  x[0] = MA_DR_MP3_VADD(x0, x1);
91014  x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f);
91015  x5 = MA_DR_MP3_VADD(x5, x6);
91016  x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f);
91017  x7 = MA_DR_MP3_VADD(x7, xt);
91018  x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f);
91019  x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f));
91020  x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f));
91021  x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f));
91022  x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6);
91023  x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f);
91024  x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f);
91025  x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f);
91026  x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f);
91027  x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f);
91028  x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f);
91029  }
91030  if (k > n - 3)
91031  {
91032 #if MA_DR_MP3_HAVE_SSE
91033 #define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
91034 #else
91035 #define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v))
91036 #endif
91037  for (i = 0; i < 7; i++, y += 4*18)
91038  {
91039  ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]);
91040  MA_DR_MP3_VSAVE2(0, t[0][i]);
91041  MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s));
91042  MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1]));
91043  MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s));
91044  }
91045  MA_DR_MP3_VSAVE2(0, t[0][7]);
91046  MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7]));
91047  MA_DR_MP3_VSAVE2(2, t[1][7]);
91048  MA_DR_MP3_VSAVE2(3, t[3][7]);
91049  } else
91050  {
91051 #define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v)
91052  for (i = 0; i < 7; i++, y += 4*18)
91053  {
91054  ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]);
91055  MA_DR_MP3_VSAVE4(0, t[0][i]);
91056  MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s));
91057  MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1]));
91058  MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s));
91059  }
91060  MA_DR_MP3_VSAVE4(0, t[0][7]);
91061  MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7]));
91062  MA_DR_MP3_VSAVE4(2, t[1][7]);
91063  MA_DR_MP3_VSAVE4(3, t[3][7]);
91064  }
91065  } else
91066 #endif
91067 #ifdef MA_DR_MP3_ONLY_SIMD
91068  {}
91069 #else
91070  for (; k < n; k++)
91071  {
91072  float t[4][8], *x, *y = grbuf + k;
91073  for (x = t[0], i = 0; i < 8; i++, x++)
91074  {
91075  float x0 = y[i*18];
91076  float x1 = y[(15 - i)*18];
91077  float x2 = y[(16 + i)*18];
91078  float x3 = y[(31 - i)*18];
91079  float t0 = x0 + x3;
91080  float t1 = x1 + x2;
91081  float t2 = (x1 - x2)*g_sec[3*i + 0];
91082  float t3 = (x0 - x3)*g_sec[3*i + 1];
91083  x[0] = t0 + t1;
91084  x[8] = (t0 - t1)*g_sec[3*i + 2];
91085  x[16] = t3 + t2;
91086  x[24] = (t3 - t2)*g_sec[3*i + 2];
91087  }
91088  for (x = t[0], i = 0; i < 4; i++, x += 8)
91089  {
91090  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;
91091  xt = x0 - x7; x0 += x7;
91092  x7 = x1 - x6; x1 += x6;
91093  x6 = x2 - x5; x2 += x5;
91094  x5 = x3 - x4; x3 += x4;
91095  x4 = x0 - x3; x0 += x3;
91096  x3 = x1 - x2; x1 += x2;
91097  x[0] = x0 + x1;
91098  x[4] = (x0 - x1)*0.70710677f;
91099  x5 = x5 + x6;
91100  x6 = (x6 + x7)*0.70710677f;
91101  x7 = x7 + xt;
91102  x3 = (x3 + x4)*0.70710677f;
91103  x5 -= x7*0.198912367f;
91104  x7 += x5*0.382683432f;
91105  x5 -= x7*0.198912367f;
91106  x0 = xt - x6; xt += x6;
91107  x[1] = (xt + x7)*0.50979561f;
91108  x[2] = (x4 + x3)*0.54119611f;
91109  x[3] = (x0 - x5)*0.60134488f;
91110  x[5] = (x0 + x5)*0.89997619f;
91111  x[6] = (x4 - x3)*1.30656302f;
91112  x[7] = (xt - x7)*2.56291556f;
91113  }
91114  for (i = 0; i < 7; i++, y += 4*18)
91115  {
91116  y[0*18] = t[0][i];
91117  y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
91118  y[2*18] = t[1][i] + t[1][i + 1];
91119  y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
91120  }
91121  y[0*18] = t[0][7];
91122  y[1*18] = t[2][7] + t[3][7];
91123  y[2*18] = t[1][7];
91124  y[3*18] = t[3][7];
91125  }
91126 #endif
91127 }
91128 #ifndef MA_DR_MP3_FLOAT_OUTPUT
91129 typedef ma_int16 ma_dr_mp3d_sample_t;
91130 static ma_int16 ma_dr_mp3d_scale_pcm(float sample)
91131 {
91132  ma_int16 s;
91133 #if MA_DR_MP3_HAVE_ARMV6
91134  ma_int32 s32 = (ma_int32)(sample + .5f);
91135  s32 -= (s32 < 0);
91136  s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32);
91137 #else
91138  if (sample >= 32766.5) return (ma_int16) 32767;
91139  if (sample <= -32767.5) return (ma_int16)-32768;
91140  s = (ma_int16)(sample + .5f);
91141  s -= (s < 0);
91142 #endif
91143  return s;
91144 }
91145 #else
91146 typedef float ma_dr_mp3d_sample_t;
91147 static float ma_dr_mp3d_scale_pcm(float sample)
91148 {
91149  return sample*(1.f/32768.f);
91150 }
91151 #endif
91152 static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z)
91153 {
91154  float a;
91155  a = (z[14*64] - z[ 0]) * 29;
91156  a += (z[ 1*64] + z[13*64]) * 213;
91157  a += (z[12*64] - z[ 2*64]) * 459;
91158  a += (z[ 3*64] + z[11*64]) * 2037;
91159  a += (z[10*64] - z[ 4*64]) * 5153;
91160  a += (z[ 5*64] + z[ 9*64]) * 6574;
91161  a += (z[ 8*64] - z[ 6*64]) * 37489;
91162  a += z[ 7*64] * 75038;
91163  pcm[0] = ma_dr_mp3d_scale_pcm(a);
91164  z += 2;
91165  a = z[14*64] * 104;
91166  a += z[12*64] * 1567;
91167  a += z[10*64] * 9727;
91168  a += z[ 8*64] * 64019;
91169  a += z[ 6*64] * -9975;
91170  a += z[ 4*64] * -45;
91171  a += z[ 2*64] * 146;
91172  a += z[ 0*64] * -5;
91173  pcm[16*nch] = ma_dr_mp3d_scale_pcm(a);
91174 }
91175 static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins)
91176 {
91177  int i;
91178  float *xr = xl + 576*(nch - 1);
91179  ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1);
91180  static const float g_win[] = {
91181  -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
91182  -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
91183  -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
91184  -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
91185  -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
91186  -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
91187  -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
91188  -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
91189  -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
91190  -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
91191  -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
91192  -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
91193  -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
91194  -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
91195  -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
91196  };
91197  float *zlin = lins + 15*64;
91198  const float *w = g_win;
91199  zlin[4*15] = xl[18*16];
91200  zlin[4*15 + 1] = xr[18*16];
91201  zlin[4*15 + 2] = xl[0];
91202  zlin[4*15 + 3] = xr[0];
91203  zlin[4*31] = xl[1 + 18*16];
91204  zlin[4*31 + 1] = xr[1 + 18*16];
91205  zlin[4*31 + 2] = xl[1];
91206  zlin[4*31 + 3] = xr[1];
91207  ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
91208  ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
91209  ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15);
91210  ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
91211 #if MA_DR_MP3_HAVE_SIMD
91212  if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--)
91213  {
91214 #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)]);
91215 #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)); }
91216 #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))); }
91217 #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))); }
91218  ma_dr_mp3_f4 a, b;
91219  zlin[4*i] = xl[18*(31 - i)];
91220  zlin[4*i + 1] = xr[18*(31 - i)];
91221  zlin[4*i + 2] = xl[1 + 18*(31 - i)];
91222  zlin[4*i + 3] = xr[1 + 18*(31 - i)];
91223  zlin[4*i + 64] = xl[1 + 18*(1 + i)];
91224  zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
91225  zlin[4*i - 64 + 2] = xl[18*(1 + i)];
91226  zlin[4*i - 64 + 3] = xr[18*(1 + i)];
91227  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)
91228  {
91229 #ifndef MA_DR_MP3_FLOAT_OUTPUT
91230 #if MA_DR_MP3_HAVE_SSE
91231  static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
91232  static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
91233  __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
91234  _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
91235  dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1);
91236  dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5);
91237  dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0);
91238  dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4);
91239  dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3);
91240  dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7);
91241  dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2);
91242  dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6);
91243 #else
91244  int16x4_t pcma, pcmb;
91245  a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f));
91246  b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f));
91247  pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0)))));
91248  pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0)))));
91249  vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
91250  vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
91251  vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
91252  vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
91253  vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
91254  vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
91255  vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
91256  vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
91257 #endif
91258 #else
91259  #if MA_DR_MP3_HAVE_SSE
91260  static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
91261  #else
91262  const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f);
91263  #endif
91264  a = MA_DR_MP3_VMUL(a, g_scale);
91265  b = MA_DR_MP3_VMUL(b, g_scale);
91266 #if MA_DR_MP3_HAVE_SSE
91267  _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
91268  _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
91269  _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
91270  _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
91271  _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
91272  _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
91273  _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
91274  _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
91275 #else
91276  vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
91277  vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
91278  vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
91279  vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
91280  vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
91281  vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
91282  vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
91283  vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
91284 #endif
91285 #endif
91286  }
91287  } else
91288 #endif
91289 #ifdef MA_DR_MP3_ONLY_SIMD
91290  {}
91291 #else
91292  for (i = 14; i >= 0; i--)
91293  {
91294 #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];
91295 #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; }
91296 #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; }
91297 #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; }
91298  float a[4], b[4];
91299  zlin[4*i] = xl[18*(31 - i)];
91300  zlin[4*i + 1] = xr[18*(31 - i)];
91301  zlin[4*i + 2] = xl[1 + 18*(31 - i)];
91302  zlin[4*i + 3] = xr[1 + 18*(31 - i)];
91303  zlin[4*(i + 16)] = xl[1 + 18*(1 + i)];
91304  zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];
91305  zlin[4*(i - 16) + 2] = xl[18*(1 + i)];
91306  zlin[4*(i - 16) + 3] = xr[18*(1 + i)];
91307  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)
91308  dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]);
91309  dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]);
91310  dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]);
91311  dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]);
91312  dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]);
91313  dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]);
91314  dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]);
91315  dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]);
91316  }
91317 #endif
91318 }
91319 static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins)
91320 {
91321  int i;
91322  for (i = 0; i < nch; i++)
91323  {
91324  ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands);
91325  }
91326  MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64);
91327  for (i = 0; i < nbands; i += 2)
91328  {
91329  ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);
91330  }
91331 #ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL
91332  if (nch == 1)
91333  {
91334  for (i = 0; i < 15*64; i += 2)
91335  {
91336  qmf_state[i] = lins[nbands*64 + i];
91337  }
91338  } else
91339 #endif
91340  {
91341  MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64);
91342  }
91343 }
91344 static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes)
91345 {
91346  int i, nmatch;
91347  for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++)
91348  {
91349  i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i);
91350  if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes)
91351  return nmatch > 0;
91352  if (!ma_dr_mp3_hdr_compare(hdr, hdr + i))
91353  return 0;
91354  }
91355  return 1;
91356 }
91357 static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)
91358 {
91359  int i, k;
91360  for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++)
91361  {
91362  if (ma_dr_mp3_hdr_valid(mp3))
91363  {
91364  int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes);
91365  int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3);
91366  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++)
91367  {
91368  if (ma_dr_mp3_hdr_compare(mp3, mp3 + k))
91369  {
91370  int fb = k - ma_dr_mp3_hdr_padding(mp3);
91371  int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k);
91372  if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb))
91373  continue;
91374  frame_and_padding = k;
91375  frame_bytes = fb;
91376  *free_format_bytes = fb;
91377  }
91378  }
91379  if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&
91380  ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||
91381  (!i && frame_and_padding == mp3_bytes))
91382  {
91383  *ptr_frame_bytes = frame_and_padding;
91384  return i;
91385  }
91386  *free_format_bytes = 0;
91387  }
91388  }
91389  *ptr_frame_bytes = 0;
91390  return mp3_bytes;
91391 }
91392 MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec)
91393 {
91394  dec->header[0] = 0;
91395 }
91396 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)
91397 {
91398  int i = 0, igr, frame_size = 0, success = 1;
91399  const ma_uint8 *hdr;
91400  ma_dr_mp3_bs bs_frame[1];
91401  ma_dr_mp3dec_scratch scratch;
91402  if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3))
91403  {
91404  frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3);
91405  if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size)))
91406  {
91407  frame_size = 0;
91408  }
91409  }
91410  if (!frame_size)
91411  {
91412  MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec));
91413  i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
91414  if (!frame_size || i + frame_size > mp3_bytes)
91415  {
91416  info->frame_bytes = i;
91417  return 0;
91418  }
91419  }
91420  hdr = mp3 + i;
91421  MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE);
91422  info->frame_bytes = i + frame_size;
91423  info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2;
91424  info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr);
91425  info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr);
91426  info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr);
91427  ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE);
91428  if (MA_DR_MP3_HDR_IS_CRC(hdr))
91429  {
91430  ma_dr_mp3_bs_get_bits(bs_frame, 16);
91431  }
91432  if (info->layer == 3)
91433  {
91434  int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);
91435  if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
91436  {
91437  ma_dr_mp3dec_init(dec);
91438  return 0;
91439  }
91440  success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
91441  if (success && pcm != NULL)
91442  {
91443  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))
91444  {
91445  MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
91446  ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
91447  ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]);
91448  }
91449  }
91450  ma_dr_mp3_L3_save_reservoir(dec, &scratch);
91451  } else
91452  {
91453 #ifdef MA_DR_MP3_ONLY_MP3
91454  return 0;
91455 #else
91456  ma_dr_mp3_L12_scale_info sci[1];
91457  if (pcm == NULL) {
91458  return ma_dr_mp3_hdr_frame_samples(hdr);
91459  }
91460  ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci);
91461  MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
91462  for (i = 0, igr = 0; igr < 3; igr++)
91463  {
91464  if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
91465  {
91466  i = 0;
91467  ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
91468  ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]);
91469  MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
91470  pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels);
91471  }
91472  if (bs_frame->pos > bs_frame->limit)
91473  {
91474  ma_dr_mp3dec_init(dec);
91475  return 0;
91476  }
91477  }
91478 #endif
91479  }
91480  return success*ma_dr_mp3_hdr_frame_samples(dec->header);
91481 }
91482 MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples)
91483 {
91484  size_t i = 0;
91485 #if MA_DR_MP3_HAVE_SIMD
91486  size_t aligned_count = num_samples & ~7;
91487  for(; i < aligned_count; i+=8)
91488  {
91489  ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f);
91490  ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale);
91491  ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale);
91492 #if MA_DR_MP3_HAVE_SSE
91493  ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f);
91494  ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f);
91495  __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
91496  _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
91497  out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0);
91498  out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1);
91499  out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2);
91500  out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3);
91501  out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4);
91502  out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5);
91503  out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6);
91504  out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7);
91505 #else
91506  int16x4_t pcma, pcmb;
91507  a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f));
91508  b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f));
91509  pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0)))));
91510  pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0)))));
91511  vst1_lane_s16(out+i , pcma, 0);
91512  vst1_lane_s16(out+i+1, pcma, 1);
91513  vst1_lane_s16(out+i+2, pcma, 2);
91514  vst1_lane_s16(out+i+3, pcma, 3);
91515  vst1_lane_s16(out+i+4, pcmb, 0);
91516  vst1_lane_s16(out+i+5, pcmb, 1);
91517  vst1_lane_s16(out+i+6, pcmb, 2);
91518  vst1_lane_s16(out+i+7, pcmb, 3);
91519 #endif
91520  }
91521 #endif
91522  for(; i < num_samples; i++)
91523  {
91524  float sample = in[i] * 32768.0f;
91525  if (sample >= 32766.5)
91526  out[i] = (ma_int16) 32767;
91527  else if (sample <= -32767.5)
91528  out[i] = (ma_int16)-32768;
91529  else
91530  {
91531  short s = (ma_int16)(sample + .5f);
91532  s -= (s < 0);
91533  out[i] = s;
91534  }
91535  }
91536 }
91537 #ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES
91538 #define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2
91539 #endif
91540 #define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384
91541 #ifndef MA_DR_MP3_DATA_CHUNK_SIZE
91542 #define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4)
91543 #endif
91544 #define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
91545 #define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi)))
91546 #ifndef MA_DR_MP3_PI_D
91547 #define MA_DR_MP3_PI_D 3.14159265358979323846264
91548 #endif
91549 #define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2
91550 static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a)
91551 {
91552  return x*(1-a) + y*a;
91553 }
91554 static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a)
91555 {
91556  float r0 = (y - x);
91557  float r1 = r0*a;
91558  return x + r1;
91559 }
91560 static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b)
91561 {
91562  for (;;) {
91563  if (b == 0) {
91564  break;
91565  } else {
91566  ma_uint32 t = a;
91567  a = b;
91568  b = t % a;
91569  }
91570  }
91571  return a;
91572 }
91573 static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData)
91574 {
91575  (void)pUserData;
91576  return MA_DR_MP3_MALLOC(sz);
91577 }
91578 static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData)
91579 {
91580  (void)pUserData;
91581  return MA_DR_MP3_REALLOC(p, sz);
91582 }
91583 static void ma_dr_mp3__free_default(void* p, void* pUserData)
91584 {
91585  (void)pUserData;
91586  MA_DR_MP3_FREE(p);
91587 }
91588 static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
91589 {
91590  if (pAllocationCallbacks == NULL) {
91591  return NULL;
91592  }
91593  if (pAllocationCallbacks->onMalloc != NULL) {
91594  return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
91595  }
91596  if (pAllocationCallbacks->onRealloc != NULL) {
91597  return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
91598  }
91599  return NULL;
91600 }
91601 static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
91602 {
91603  if (pAllocationCallbacks == NULL) {
91604  return NULL;
91605  }
91606  if (pAllocationCallbacks->onRealloc != NULL) {
91607  return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
91608  }
91609  if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
91610  void* p2;
91611  p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
91612  if (p2 == NULL) {
91613  return NULL;
91614  }
91615  if (p != NULL) {
91616  MA_DR_MP3_COPY_MEMORY(p2, p, szOld);
91617  pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
91618  }
91619  return p2;
91620  }
91621  return NULL;
91622 }
91623 static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
91624 {
91625  if (p == NULL || pAllocationCallbacks == NULL) {
91626  return;
91627  }
91628  if (pAllocationCallbacks->onFree != NULL) {
91629  pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
91630  }
91631 }
91632 static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks)
91633 {
91634  if (pAllocationCallbacks != NULL) {
91635  return *pAllocationCallbacks;
91636  } else {
91637  ma_allocation_callbacks allocationCallbacks;
91638  allocationCallbacks.pUserData = NULL;
91639  allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default;
91640  allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default;
91641  allocationCallbacks.onFree = ma_dr_mp3__free_default;
91642  return allocationCallbacks;
91643  }
91644 }
91645 static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead)
91646 {
91647  size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
91648  pMP3->streamCursor += bytesRead;
91649  return bytesRead;
91650 }
91651 static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin)
91652 {
91653  MA_DR_MP3_ASSERT(offset >= 0);
91654  if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
91655  return MA_FALSE;
91656  }
91657  if (origin == ma_dr_mp3_seek_origin_start) {
91658  pMP3->streamCursor = (ma_uint64)offset;
91659  } else {
91660  pMP3->streamCursor += offset;
91661  }
91662  return MA_TRUE;
91663 }
91664 static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin)
91665 {
91666  if (offset <= 0x7FFFFFFF) {
91667  return ma_dr_mp3__on_seek(pMP3, (int)offset, origin);
91668  }
91669  if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) {
91670  return MA_FALSE;
91671  }
91672  offset -= 0x7FFFFFFF;
91673  while (offset > 0) {
91674  if (offset <= 0x7FFFFFFF) {
91675  if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) {
91676  return MA_FALSE;
91677  }
91678  offset = 0;
91679  } else {
91680  if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) {
91681  return MA_FALSE;
91682  }
91683  offset -= 0x7FFFFFFF;
91684  }
91685  }
91686  return MA_TRUE;
91687 }
91688 static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames)
91689 {
91690  ma_uint32 pcmFramesRead = 0;
91691  MA_DR_MP3_ASSERT(pMP3 != NULL);
91692  MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
91693  if (pMP3->atEnd) {
91694  return 0;
91695  }
91696  for (;;) {
91697  ma_dr_mp3dec_frame_info info;
91698  if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) {
91699  size_t bytesRead;
91700  if (pMP3->pData != NULL) {
91701  MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
91702  }
91703  pMP3->dataConsumed = 0;
91704  if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) {
91705  ma_uint8* pNewData;
91706  size_t newDataCap;
91707  newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE;
91708  pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
91709  if (pNewData == NULL) {
91710  return 0;
91711  }
91712  pMP3->pData = pNewData;
91713  pMP3->dataCapacity = newDataCap;
91714  }
91715  bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
91716  if (bytesRead == 0) {
91717  if (pMP3->dataSize == 0) {
91718  pMP3->atEnd = MA_TRUE;
91719  return 0;
91720  }
91721  }
91722  pMP3->dataSize += bytesRead;
91723  }
91724  if (pMP3->dataSize > INT_MAX) {
91725  pMP3->atEnd = MA_TRUE;
91726  return 0;
91727  }
91728  MA_DR_MP3_ASSERT(pMP3->pData != NULL);
91729  MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0);
91730  if (pMP3->pData == NULL) {
91731  return 0;
91732  }
91733  pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);
91734  if (info.frame_bytes > 0) {
91735  pMP3->dataConsumed += (size_t)info.frame_bytes;
91736  pMP3->dataSize -= (size_t)info.frame_bytes;
91737  }
91738  if (pcmFramesRead > 0) {
91739  pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header);
91740  pMP3->pcmFramesConsumedInMP3Frame = 0;
91741  pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
91742  pMP3->mp3FrameChannels = info.channels;
91743  pMP3->mp3FrameSampleRate = info.hz;
91744  break;
91745  } else if (info.frame_bytes == 0) {
91746  size_t bytesRead;
91747  MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
91748  pMP3->dataConsumed = 0;
91749  if (pMP3->dataCapacity == pMP3->dataSize) {
91750  ma_uint8* pNewData;
91751  size_t newDataCap;
91752  newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE;
91753  pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
91754  if (pNewData == NULL) {
91755  return 0;
91756  }
91757  pMP3->pData = pNewData;
91758  pMP3->dataCapacity = newDataCap;
91759  }
91760  bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
91761  if (bytesRead == 0) {
91762  pMP3->atEnd = MA_TRUE;
91763  return 0;
91764  }
91765  pMP3->dataSize += bytesRead;
91766  }
91767  };
91768  return pcmFramesRead;
91769 }
91770 static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames)
91771 {
91772  ma_uint32 pcmFramesRead = 0;
91773  ma_dr_mp3dec_frame_info info;
91774  MA_DR_MP3_ASSERT(pMP3 != NULL);
91775  MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL);
91776  if (pMP3->atEnd) {
91777  return 0;
91778  }
91779  for (;;) {
91780  pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);
91781  if (pcmFramesRead > 0) {
91782  pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header);
91783  pMP3->pcmFramesConsumedInMP3Frame = 0;
91784  pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
91785  pMP3->mp3FrameChannels = info.channels;
91786  pMP3->mp3FrameSampleRate = info.hz;
91787  break;
91788  } else if (info.frame_bytes > 0) {
91789  pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
91790  } else {
91791  break;
91792  }
91793  }
91794  pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
91795  return pcmFramesRead;
91796 }
91797 static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames)
91798 {
91799  if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
91800  return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames);
91801  } else {
91802  return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames);
91803  }
91804 }
91805 static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3)
91806 {
91807  MA_DR_MP3_ASSERT(pMP3 != NULL);
91808  return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames);
91809 }
91810 #if 0
91811 static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3)
91812 {
91813  ma_uint32 pcmFrameCount;
91814  MA_DR_MP3_ASSERT(pMP3 != NULL);
91815  pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL);
91816  if (pcmFrameCount == 0) {
91817  return 0;
91818  }
91819  pMP3->currentPCMFrame += pcmFrameCount;
91820  pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
91821  pMP3->pcmFramesRemainingInMP3Frame = 0;
91822  return pcmFrameCount;
91823 }
91824 #endif
91825 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)
91826 {
91827  MA_DR_MP3_ASSERT(pMP3 != NULL);
91828  MA_DR_MP3_ASSERT(onRead != NULL);
91829  ma_dr_mp3dec_init(&pMP3->decoder);
91830  pMP3->onRead = onRead;
91831  pMP3->onSeek = onSeek;
91832  pMP3->pUserData = pUserData;
91833  pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
91834  if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
91835  return MA_FALSE;
91836  }
91837  if (ma_dr_mp3_decode_next_frame(pMP3) == 0) {
91838  ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
91839  return MA_FALSE;
91840  }
91841  pMP3->channels = pMP3->mp3FrameChannels;
91842  pMP3->sampleRate = pMP3->mp3FrameSampleRate;
91843  return MA_TRUE;
91844 }
91845 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)
91846 {
91847  if (pMP3 == NULL || onRead == NULL) {
91848  return MA_FALSE;
91849  }
91850  MA_DR_MP3_ZERO_OBJECT(pMP3);
91851  return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);
91852 }
91853 static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
91854 {
91855  ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;
91856  size_t bytesRemaining;
91857  MA_DR_MP3_ASSERT(pMP3 != NULL);
91858  MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
91859  bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
91860  if (bytesToRead > bytesRemaining) {
91861  bytesToRead = bytesRemaining;
91862  }
91863  if (bytesToRead > 0) {
91864  MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
91865  pMP3->memory.currentReadPos += bytesToRead;
91866  }
91867  return bytesToRead;
91868 }
91869 static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin)
91870 {
91871  ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;
91872  MA_DR_MP3_ASSERT(pMP3 != NULL);
91873  if (origin == ma_dr_mp3_seek_origin_current) {
91874  if (byteOffset > 0) {
91875  if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
91876  byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos);
91877  }
91878  } else {
91879  if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
91880  byteOffset = -(int)pMP3->memory.currentReadPos;
91881  }
91882  }
91883  pMP3->memory.currentReadPos += byteOffset;
91884  } else {
91885  if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) {
91886  pMP3->memory.currentReadPos = byteOffset;
91887  } else {
91888  pMP3->memory.currentReadPos = pMP3->memory.dataSize;
91889  }
91890  }
91891  return MA_TRUE;
91892 }
91893 MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
91894 {
91895  if (pMP3 == NULL) {
91896  return MA_FALSE;
91897  }
91898  MA_DR_MP3_ZERO_OBJECT(pMP3);
91899  if (pData == NULL || dataSize == 0) {
91900  return MA_FALSE;
91901  }
91902  pMP3->memory.pData = (const ma_uint8*)pData;
91903  pMP3->memory.dataSize = dataSize;
91904  pMP3->memory.currentReadPos = 0;
91905  return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks);
91906 }
91907 #ifndef MA_DR_MP3_NO_STDIO
91908 #include <stdio.h>
91909 #include <wchar.h>
91910 static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
91911 {
91912  return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
91913 }
91914 static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin)
91915 {
91916  return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
91917 }
91918 MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks)
91919 {
91920  ma_bool32 result;
91921  FILE* pFile;
91922  if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) {
91923  return MA_FALSE;
91924  }
91925  result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
91926  if (result != MA_TRUE) {
91927  fclose(pFile);
91928  return result;
91929  }
91930  return MA_TRUE;
91931 }
91932 MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks)
91933 {
91934  ma_bool32 result;
91935  FILE* pFile;
91936  if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
91937  return MA_FALSE;
91938  }
91939  result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
91940  if (result != MA_TRUE) {
91941  fclose(pFile);
91942  return result;
91943  }
91944  return MA_TRUE;
91945 }
91946 #endif
91947 MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3)
91948 {
91949  if (pMP3 == NULL) {
91950  return;
91951  }
91952 #ifndef MA_DR_MP3_NO_STDIO
91953  if (pMP3->onRead == ma_dr_mp3__on_read_stdio) {
91954  FILE* pFile = (FILE*)pMP3->pUserData;
91955  if (pFile != NULL) {
91956  fclose(pFile);
91957  pMP3->pUserData = NULL;
91958  }
91959  }
91960 #endif
91961  ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
91962 }
91963 #if defined(MA_DR_MP3_FLOAT_OUTPUT)
91964 static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount)
91965 {
91966  ma_uint64 i;
91967  ma_uint64 i4;
91968  ma_uint64 sampleCount4;
91969  i = 0;
91970  sampleCount4 = sampleCount >> 2;
91971  for (i4 = 0; i4 < sampleCount4; i4 += 1) {
91972  float x0 = src[i+0];
91973  float x1 = src[i+1];
91974  float x2 = src[i+2];
91975  float x3 = src[i+3];
91976  x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
91977  x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
91978  x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
91979  x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
91980  x0 = x0 * 32767.0f;
91981  x1 = x1 * 32767.0f;
91982  x2 = x2 * 32767.0f;
91983  x3 = x3 * 32767.0f;
91984  dst[i+0] = (ma_int16)x0;
91985  dst[i+1] = (ma_int16)x1;
91986  dst[i+2] = (ma_int16)x2;
91987  dst[i+3] = (ma_int16)x3;
91988  i += 4;
91989  }
91990  for (; i < sampleCount; i += 1) {
91991  float x = src[i];
91992  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
91993  x = x * 32767.0f;
91994  dst[i] = (ma_int16)x;
91995  }
91996 }
91997 #endif
91998 #if !defined(MA_DR_MP3_FLOAT_OUTPUT)
91999 static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount)
92000 {
92001  ma_uint64 i;
92002  for (i = 0; i < sampleCount; i += 1) {
92003  float x = (float)src[i];
92004  x = x * 0.000030517578125f;
92005  dst[i] = x;
92006  }
92007 }
92008 #endif
92009 static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut)
92010 {
92011  ma_uint64 totalFramesRead = 0;
92012  MA_DR_MP3_ASSERT(pMP3 != NULL);
92013  MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
92014  while (framesToRead > 0) {
92015  ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
92016  if (pBufferOut != NULL) {
92017  #if defined(MA_DR_MP3_FLOAT_OUTPUT)
92018  float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
92019  float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
92020  MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
92021  #else
92022  ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels);
92023  ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
92024  MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels);
92025  #endif
92026  }
92027  pMP3->currentPCMFrame += framesToConsume;
92028  pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
92029  pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
92030  totalFramesRead += framesToConsume;
92031  framesToRead -= framesToConsume;
92032  if (framesToRead == 0) {
92033  break;
92034  }
92035  MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
92036  if (ma_dr_mp3_decode_next_frame(pMP3) == 0) {
92037  break;
92038  }
92039  }
92040  return totalFramesRead;
92041 }
92042 MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut)
92043 {
92044  if (pMP3 == NULL || pMP3->onRead == NULL) {
92045  return 0;
92046  }
92047 #if defined(MA_DR_MP3_FLOAT_OUTPUT)
92048  return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
92049 #else
92050  {
92051  ma_int16 pTempS16[8192];
92052  ma_uint64 totalPCMFramesRead = 0;
92053  while (totalPCMFramesRead < framesToRead) {
92054  ma_uint64 framesJustRead;
92055  ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
92056  ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels;
92057  if (framesToReadNow > framesRemaining) {
92058  framesToReadNow = framesRemaining;
92059  }
92060  framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
92061  if (framesJustRead == 0) {
92062  break;
92063  }
92064  ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
92065  totalPCMFramesRead += framesJustRead;
92066  }
92067  return totalPCMFramesRead;
92068  }
92069 #endif
92070 }
92071 MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut)
92072 {
92073  if (pMP3 == NULL || pMP3->onRead == NULL) {
92074  return 0;
92075  }
92076 #if !defined(MA_DR_MP3_FLOAT_OUTPUT)
92077  return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
92078 #else
92079  {
92080  float pTempF32[4096];
92081  ma_uint64 totalPCMFramesRead = 0;
92082  while (totalPCMFramesRead < framesToRead) {
92083  ma_uint64 framesJustRead;
92084  ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
92085  ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels;
92086  if (framesToReadNow > framesRemaining) {
92087  framesToReadNow = framesRemaining;
92088  }
92089  framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
92090  if (framesJustRead == 0) {
92091  break;
92092  }
92093  ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
92094  totalPCMFramesRead += framesJustRead;
92095  }
92096  return totalPCMFramesRead;
92097  }
92098 #endif
92099 }
92100 static void ma_dr_mp3_reset(ma_dr_mp3* pMP3)
92101 {
92102  MA_DR_MP3_ASSERT(pMP3 != NULL);
92103  pMP3->pcmFramesConsumedInMP3Frame = 0;
92104  pMP3->pcmFramesRemainingInMP3Frame = 0;
92105  pMP3->currentPCMFrame = 0;
92106  pMP3->dataSize = 0;
92107  pMP3->atEnd = MA_FALSE;
92108  ma_dr_mp3dec_init(&pMP3->decoder);
92109 }
92110 static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3)
92111 {
92112  MA_DR_MP3_ASSERT(pMP3 != NULL);
92113  MA_DR_MP3_ASSERT(pMP3->onSeek != NULL);
92114  if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) {
92115  return MA_FALSE;
92116  }
92117  ma_dr_mp3_reset(pMP3);
92118  return MA_TRUE;
92119 }
92120 static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset)
92121 {
92122  ma_uint64 framesRead;
92123 #if defined(MA_DR_MP3_FLOAT_OUTPUT)
92124  framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
92125 #else
92126  framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
92127 #endif
92128  if (framesRead != frameOffset) {
92129  return MA_FALSE;
92130  }
92131  return MA_TRUE;
92132 }
92133 static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
92134 {
92135  MA_DR_MP3_ASSERT(pMP3 != NULL);
92136  if (frameIndex == pMP3->currentPCMFrame) {
92137  return MA_TRUE;
92138  }
92139  if (frameIndex < pMP3->currentPCMFrame) {
92140  if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
92141  return MA_FALSE;
92142  }
92143  }
92144  MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
92145  return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
92146 }
92147 static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex)
92148 {
92149  ma_uint32 iSeekPoint;
92150  MA_DR_MP3_ASSERT(pSeekPointIndex != NULL);
92151  *pSeekPointIndex = 0;
92152  if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
92153  return MA_FALSE;
92154  }
92155  for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
92156  if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
92157  break;
92158  }
92159  *pSeekPointIndex = iSeekPoint;
92160  }
92161  return MA_TRUE;
92162 }
92163 static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
92164 {
92165  ma_dr_mp3_seek_point seekPoint;
92166  ma_uint32 priorSeekPointIndex;
92167  ma_uint16 iMP3Frame;
92168  ma_uint64 leftoverFrames;
92169  MA_DR_MP3_ASSERT(pMP3 != NULL);
92170  MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL);
92171  MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0);
92172  if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
92173  seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
92174  } else {
92175  seekPoint.seekPosInBytes = 0;
92176  seekPoint.pcmFrameIndex = 0;
92177  seekPoint.mp3FramesToDiscard = 0;
92178  seekPoint.pcmFramesToDiscard = 0;
92179  }
92180  if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) {
92181  return MA_FALSE;
92182  }
92183  ma_dr_mp3_reset(pMP3);
92184  for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
92185  ma_uint32 pcmFramesRead;
92186  ma_dr_mp3d_sample_t* pPCMFrames;
92187  pPCMFrames = NULL;
92188  if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
92189  pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames;
92190  }
92191  pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames);
92192  if (pcmFramesRead == 0) {
92193  return MA_FALSE;
92194  }
92195  }
92196  pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
92197  leftoverFrames = frameIndex - pMP3->currentPCMFrame;
92198  return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
92199 }
92200 MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
92201 {
92202  if (pMP3 == NULL || pMP3->onSeek == NULL) {
92203  return MA_FALSE;
92204  }
92205  if (frameIndex == 0) {
92206  return ma_dr_mp3_seek_to_start_of_stream(pMP3);
92207  }
92208  if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
92209  return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
92210  } else {
92211  return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
92212  }
92213 }
92214 MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount)
92215 {
92216  ma_uint64 currentPCMFrame;
92217  ma_uint64 totalPCMFrameCount;
92218  ma_uint64 totalMP3FrameCount;
92219  if (pMP3 == NULL) {
92220  return MA_FALSE;
92221  }
92222  if (pMP3->onSeek == NULL) {
92223  return MA_FALSE;
92224  }
92225  currentPCMFrame = pMP3->currentPCMFrame;
92226  if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
92227  return MA_FALSE;
92228  }
92229  totalPCMFrameCount = 0;
92230  totalMP3FrameCount = 0;
92231  for (;;) {
92232  ma_uint32 pcmFramesInCurrentMP3Frame;
92233  pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL);
92234  if (pcmFramesInCurrentMP3Frame == 0) {
92235  break;
92236  }
92237  totalPCMFrameCount += pcmFramesInCurrentMP3Frame;
92238  totalMP3FrameCount += 1;
92239  }
92240  if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
92241  return MA_FALSE;
92242  }
92243  if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
92244  return MA_FALSE;
92245  }
92246  if (pMP3FrameCount != NULL) {
92247  *pMP3FrameCount = totalMP3FrameCount;
92248  }
92249  if (pPCMFrameCount != NULL) {
92250  *pPCMFrameCount = totalPCMFrameCount;
92251  }
92252  return MA_TRUE;
92253 }
92254 MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3)
92255 {
92256  ma_uint64 totalPCMFrameCount;
92257  if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
92258  return 0;
92259  }
92260  return totalPCMFrameCount;
92261 }
92262 MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3)
92263 {
92264  ma_uint64 totalMP3FrameCount;
92265  if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
92266  return 0;
92267  }
92268  return totalMP3FrameCount;
92269 }
92270 static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
92271 {
92272  float srcRatio;
92273  float pcmFrameCountOutF;
92274  ma_uint32 pcmFrameCountOut;
92275  srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
92276  MA_DR_MP3_ASSERT(srcRatio > 0);
92277  pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
92278  pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF;
92279  *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
92280  *pRunningPCMFrameCount += pcmFrameCountOut;
92281 }
92282 typedef struct
92283 {
92284  ma_uint64 bytePos;
92285  ma_uint64 pcmFrameIndex;
92286 } ma_dr_mp3__seeking_mp3_frame_info;
92287 MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints)
92288 {
92289  ma_uint32 seekPointCount;
92290  ma_uint64 currentPCMFrame;
92291  ma_uint64 totalMP3FrameCount;
92292  ma_uint64 totalPCMFrameCount;
92293  if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
92294  return MA_FALSE;
92295  }
92296  seekPointCount = *pSeekPointCount;
92297  if (seekPointCount == 0) {
92298  return MA_FALSE;
92299  }
92300  currentPCMFrame = pMP3->currentPCMFrame;
92301  if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
92302  return MA_FALSE;
92303  }
92304  if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) {
92305  seekPointCount = 1;
92306  pSeekPoints[0].seekPosInBytes = 0;
92307  pSeekPoints[0].pcmFrameIndex = 0;
92308  pSeekPoints[0].mp3FramesToDiscard = 0;
92309  pSeekPoints[0].pcmFramesToDiscard = 0;
92310  } else {
92311  ma_uint64 pcmFramesBetweenSeekPoints;
92312  ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1];
92313  ma_uint64 runningPCMFrameCount = 0;
92314  float runningPCMFrameCountFractionalPart = 0;
92315  ma_uint64 nextTargetPCMFrame;
92316  ma_uint32 iMP3Frame;
92317  ma_uint32 iSeekPoint;
92318  if (seekPointCount > totalMP3FrameCount-1) {
92319  seekPointCount = (ma_uint32)totalMP3FrameCount-1;
92320  }
92321  pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
92322  if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
92323  return MA_FALSE;
92324  }
92325  for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
92326  ma_uint32 pcmFramesInCurrentMP3FrameIn;
92327  MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
92328  mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
92329  mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
92330  pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL);
92331  if (pcmFramesInCurrentMP3FrameIn == 0) {
92332  return MA_FALSE;
92333  }
92334  ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
92335  }
92336  nextTargetPCMFrame = 0;
92337  for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
92338  nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
92339  for (;;) {
92340  if (nextTargetPCMFrame < runningPCMFrameCount) {
92341  pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
92342  pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
92343  pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES;
92344  pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
92345  break;
92346  } else {
92347  size_t i;
92348  ma_uint32 pcmFramesInCurrentMP3FrameIn;
92349  for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) {
92350  mp3FrameInfo[i] = mp3FrameInfo[i+1];
92351  }
92352  mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
92353  mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
92354  pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL);
92355  if (pcmFramesInCurrentMP3FrameIn == 0) {
92356  pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
92357  pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
92358  pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES;
92359  pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
92360  break;
92361  }
92362  ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
92363  }
92364  }
92365  }
92366  if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
92367  return MA_FALSE;
92368  }
92369  if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
92370  return MA_FALSE;
92371  }
92372  }
92373  *pSeekPointCount = seekPointCount;
92374  return MA_TRUE;
92375 }
92376 MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints)
92377 {
92378  if (pMP3 == NULL) {
92379  return MA_FALSE;
92380  }
92381  if (seekPointCount == 0 || pSeekPoints == NULL) {
92382  pMP3->seekPointCount = 0;
92383  pMP3->pSeekPoints = NULL;
92384  } else {
92385  pMP3->seekPointCount = seekPointCount;
92386  pMP3->pSeekPoints = pSeekPoints;
92387  }
92388  return MA_TRUE;
92389 }
92390 static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount)
92391 {
92392  ma_uint64 totalFramesRead = 0;
92393  ma_uint64 framesCapacity = 0;
92394  float* pFrames = NULL;
92395  float temp[4096];
92396  MA_DR_MP3_ASSERT(pMP3 != NULL);
92397  for (;;) {
92398  ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels;
92399  ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
92400  if (framesJustRead == 0) {
92401  break;
92402  }
92403  if (framesCapacity < totalFramesRead + framesJustRead) {
92404  ma_uint64 oldFramesBufferSize;
92405  ma_uint64 newFramesBufferSize;
92406  ma_uint64 newFramesCap;
92407  float* pNewFrames;
92408  newFramesCap = framesCapacity * 2;
92409  if (newFramesCap < totalFramesRead + framesJustRead) {
92410  newFramesCap = totalFramesRead + framesJustRead;
92411  }
92412  oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
92413  newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
92414  if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) {
92415  break;
92416  }
92417  pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
92418  if (pNewFrames == NULL) {
92419  ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
92420  break;
92421  }
92422  pFrames = pNewFrames;
92423  framesCapacity = newFramesCap;
92424  }
92425  MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
92426  totalFramesRead += framesJustRead;
92427  if (framesJustRead != framesToReadRightNow) {
92428  break;
92429  }
92430  }
92431  if (pConfig != NULL) {
92432  pConfig->channels = pMP3->channels;
92433  pConfig->sampleRate = pMP3->sampleRate;
92434  }
92435  ma_dr_mp3_uninit(pMP3);
92436  if (pTotalFrameCount) {
92437  *pTotalFrameCount = totalFramesRead;
92438  }
92439  return pFrames;
92440 }
92441 static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount)
92442 {
92443  ma_uint64 totalFramesRead = 0;
92444  ma_uint64 framesCapacity = 0;
92445  ma_int16* pFrames = NULL;
92446  ma_int16 temp[4096];
92447  MA_DR_MP3_ASSERT(pMP3 != NULL);
92448  for (;;) {
92449  ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels;
92450  ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
92451  if (framesJustRead == 0) {
92452  break;
92453  }
92454  if (framesCapacity < totalFramesRead + framesJustRead) {
92455  ma_uint64 newFramesBufferSize;
92456  ma_uint64 oldFramesBufferSize;
92457  ma_uint64 newFramesCap;
92458  ma_int16* pNewFrames;
92459  newFramesCap = framesCapacity * 2;
92460  if (newFramesCap < totalFramesRead + framesJustRead) {
92461  newFramesCap = totalFramesRead + framesJustRead;
92462  }
92463  oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16);
92464  newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16);
92465  if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) {
92466  break;
92467  }
92468  pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
92469  if (pNewFrames == NULL) {
92470  ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
92471  break;
92472  }
92473  pFrames = pNewFrames;
92474  framesCapacity = newFramesCap;
92475  }
92476  MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16)));
92477  totalFramesRead += framesJustRead;
92478  if (framesJustRead != framesToReadRightNow) {
92479  break;
92480  }
92481  }
92482  if (pConfig != NULL) {
92483  pConfig->channels = pMP3->channels;
92484  pConfig->sampleRate = pMP3->sampleRate;
92485  }
92486  ma_dr_mp3_uninit(pMP3);
92487  if (pTotalFrameCount) {
92488  *pTotalFrameCount = totalFramesRead;
92489  }
92490  return pFrames;
92491 }
92492 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)
92493 {
92494  ma_dr_mp3 mp3;
92495  if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
92496  return NULL;
92497  }
92498  return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
92499 }
92500 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)
92501 {
92502  ma_dr_mp3 mp3;
92503  if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
92504  return NULL;
92505  }
92506  return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
92507 }
92508 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)
92509 {
92510  ma_dr_mp3 mp3;
92511  if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
92512  return NULL;
92513  }
92514  return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
92515 }
92516 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)
92517 {
92518  ma_dr_mp3 mp3;
92519  if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
92520  return NULL;
92521  }
92522  return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
92523 }
92524 #ifndef MA_DR_MP3_NO_STDIO
92525 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)
92526 {
92527  ma_dr_mp3 mp3;
92528  if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
92529  return NULL;
92530  }
92531  return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
92532 }
92533 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)
92534 {
92535  ma_dr_mp3 mp3;
92536  if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
92537  return NULL;
92538  }
92539  return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
92540 }
92541 #endif
92542 MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
92543 {
92544  if (pAllocationCallbacks != NULL) {
92545  return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks);
92546  } else {
92547  return ma_dr_mp3__malloc_default(sz, NULL);
92548  }
92549 }
92550 MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
92551 {
92552  if (pAllocationCallbacks != NULL) {
92553  ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks);
92554  } else {
92555  ma_dr_mp3__free_default(p, NULL);
92556  }
92557 }
92558 #endif
92559 /* dr_mp3_c end */
92560 #endif /* MA_DR_MP3_IMPLEMENTATION */
92561 #endif /* MA_NO_MP3 */
92562 
92563 
92564 /* End globally disabled warnings. */
92565 #if defined(_MSC_VER)
92566  #pragma warning(pop)
92567 #endif
92568 
92569 #endif /* miniaudio_c */
92570 #endif /* MINIAUDIO_IMPLEMENTATION */
92571 
92572 
92573 /*
92574 This software is available as a choice of the following licenses. Choose
92575 whichever you prefer.
92576 
92577 ===============================================================================
92578 ALTERNATIVE 1 - Public Domain (www.unlicense.org)
92579 ===============================================================================
92580 This is free and unencumbered software released into the public domain.
92581 
92582 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
92583 software, either in source code form or as a compiled binary, for any purpose,
92584 commercial or non-commercial, and by any means.
92585 
92586 In jurisdictions that recognize copyright laws, the author or authors of this
92587 software dedicate any and all copyright interest in the software to the public
92588 domain. We make this dedication for the benefit of the public at large and to
92589 the detriment of our heirs and successors. We intend this dedication to be an
92590 overt act of relinquishment in perpetuity of all present and future rights to
92591 this software under copyright law.
92592 
92593 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
92594 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92595 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
92596 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
92597 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
92598 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
92599 
92600 For more information, please refer to <http://unlicense.org/>
92601 
92602 ===============================================================================
92603 ALTERNATIVE 2 - MIT No Attribution
92604 ===============================================================================
92605 Copyright 2023 David Reid
92606 
92607 Permission is hereby granted, free of charge, to any person obtaining a copy of
92608 this software and associated documentation files (the "Software"), to deal in
92609 the Software without restriction, including without limitation the rights to
92610 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
92611 of the Software, and to permit persons to whom the Software is furnished to do
92612 so.
92613 
92614 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
92615 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92616 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
92617 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
92618 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
92619 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
92620 SOFTWARE.
92621 */
Definition: miniaudio.h:4622
Definition: miniaudio.h:5287
Definition: miniaudio.h:10046
Definition: miniaudio.h:10444
Definition: miniaudio.h:6985
Definition: miniaudio.h:6297
Definition: miniaudio.h:10787
Definition: miniaudio.h:5080
Definition: miniaudio.h:10573
Definition: miniaudio.h:10831
Definition: miniaudio.h:10868
Definition: miniaudio.h:9876
Definition: miniaudio.h:4789
Definition: miniaudio.h:7301
Definition: miniaudio.h:5968
Definition: miniaudio.h:10251
Definition: miniaudio.h:6723
Definition: miniaudio.h:5114
Definition: miniaudio.h:10724
Definition: miniaudio.h:10671
Definition: miniaudio.h:7276
Definition: miniaudio.h:4826
Definition: miniaudio.h:10349
Definition: miniaudio.h:11063
Definition: miniaudio.h:9899
Definition: miniaudio.h:11158
Definition: miniaudio.h:10616
Definition: miniaudio.h:6302
Definition: miniaudio.h:10634
Definition: miniaudio.h:4325
Definition: miniaudio.h:9916
Definition: miniaudio.h:10946
Definition: miniaudio.h:5864
Definition: miniaudio.h:10914
Definition: jar_mod.h:108
Definition: miniaudio.h:10412
Definition: miniaudio.h:4601
Definition: miniaudio.h:10937
Definition: miniaudio.h:5937
Definition: miniaudio.h:4557
Definition: miniaudio.h:10321
Definition: miniaudio.h:5087
Definition: miniaudio.h:5781
Definition: miniaudio.h:7708
Definition: miniaudio.h:10845
Definition: miniaudio.h:10891
Definition: miniaudio.h:4418
Definition: miniaudio.h:10363
Definition: jar_mod.h:139
Definition: miniaudio.h:10114
Definition: miniaudio.h:10777
Definition: miniaudio.h:7243
Definition: miniaudio.h:6488
Definition: miniaudio.h:9809
Definition: miniaudio.h:10296
Definition: miniaudio.h:4800
Definition: miniaudio.h:4735
Definition: miniaudio.h:5163
Definition: miniaudio.h:5275
Definition: miniaudio.h:10755
Definition: miniaudio.h:4513
Definition: miniaudio.h:4648
Definition: miniaudio.h:4429
Definition: miniaudio.h:10733
Definition: miniaudio.h:5794
Definition: miniaudio.h:10799
Definition: miniaudio.h:6355
Definition: miniaudio.h:5365
Definition: miniaudio.h:4837
Definition: miniaudio.h:5488
Definition: miniaudio.h:10178
Definition: miniaudio.h:10102
Definition: miniaudio.h:4856
Definition: miniaudio.h:6497
Definition: miniaudio.h:5569
Definition: miniaudio.h:6979
Definition: miniaudio.h:7020
Definition: miniaudio.h:6042
Definition: miniaudio.h:9814
Definition: miniaudio.h:5023
Definition: miniaudio.h:5348
Definition: miniaudio.h:4724
Definition: miniaudio.h:5052
Definition: miniaudio.h:10131
Definition: miniaudio.h:4537
Definition: miniaudio.h:5839
Definition: miniaudio.h:9836
Definition: miniaudio.h:5876
Definition: miniaudio.h:4504
Definition: miniaudio.h:4775
Definition: miniaudio.h:11174
Definition: miniaudio.h:5188
Definition: miniaudio.h:4637
Definition: miniaudio.h:6000
Definition: miniaudio.h:10764
Definition: miniaudio.h:4962
Definition: stb_vorbis.c:129
Definition: miniaudio.h:10989
Definition: miniaudio.h:6236
Definition: miniaudio.h:10658
Definition: miniaudio.h:4543
Definition: miniaudio.h:7037
Definition: miniaudio.h:5328
Definition: miniaudio.h:4930
Definition: miniaudio.h:4678
Definition: miniaudio.h:6289
Definition: miniaudio.h:11207
Definition: miniaudio.h:10900
Definition: miniaudio.h:7226
Definition: miniaudio.h:4589
Definition: miniaudio.h:10923
Definition: miniaudio.h:10808
Definition: miniaudio.h:4317
Definition: miniaudio.h:5537
Definition: miniaudio.h:4710
Definition: miniaudio.h:6689
Definition: miniaudio.h:5502
Definition: miniaudio.h:11137
Definition: miniaudio.h:9885
Definition: miniaudio.h:4887
Definition: miniaudio.h:5061
Definition: miniaudio.h:5945
Definition: miniaudio.h:10969
Definition: miniaudio.h:4899
Definition: miniaudio.h:10425
Definition: miniaudio.h:4948
Definition: miniaudio.h:4982
Definition: miniaudio.h:5802
Definition: miniaudio.h:10057
Definition: miniaudio.h:5129
Definition: miniaudio.h:6202
Definition: miniaudio.h:5911
Definition: stb_vorbis.c:785
Definition: miniaudio.h:7145
Definition: miniaudio.h:5034
Definition: miniaudio.h:6698
Definition: miniaudio.h:4991
Definition: miniaudio.h:10166
Definition: miniaudio.h:11045
Definition: miniaudio.h:4868
Definition: miniaudio.h:5918
Definition: miniaudio.h:10854
Definition: miniaudio.h:10877
Definition: miniaudio.h:10960
Definition: miniaudio.h:4690
Definition: miniaudio.h:6251
Definition: miniaudio.h:4764
Definition: miniaudio.h:10822
Definition: miniaudio.h:10143
Definition: miniaudio.h:11108
Definition: miniaudio.h:4918
Definition: miniaudio.h:6223
Definition: miniaudio.h:10980
Definition: miniaudio.h:10384